浅析Node的nextTick

看thinkjs源码的时候发现下面这段代码。

  cluster.on('exit', worker => {
    think.log(new Error(think.locale('WORKER_DIED', worker.process.pid)), 'THINK');
    process.nextTick(() => cluster.fork());
  });

这段代码的意思很简单,就是cluster挂了以后重新fork一个。
  但是注意到其中的process.nextTick(() => cluster.fork());这行,刚开始想了一下没有理解为什么不直接fork,后面仔细想了一下,发现如果直接fork,在fork的过程中又出现错误导致进程退出,而cluster又监听到exit的事件,就会不断的重复这个过程,阻塞Node进程。
  如果使用process.nextTick(() => cluster.fork());则不会阻塞Node的事件循环,只会在Event Loopclose callbacks阶段执行fork,即使程序一直fork失败也不会导致程序假死。(如果有疑问可以阅读文章末的扩展阅读)。
  下面的Demo说明了为什么使用了nextTick不会导致程序假死。

var EventEmitter = require('events').EventEmitter; 
var event = new EventEmitter();

var count = 0;
var num = 10;

event.on('some_event', function() { 
  count++;
  console.log('some_event 事件触发' + count);
  if (count < num) {
    event.emit('some_event')
  }
});

event.emit('some_event'); 

console.log('what ?')

运行这段代码就会输出

some_event 事件触发1
some_event 事件触发2
some_event 事件触发3
some_event 事件触发4
some_event 事件触发5
some_event 事件触发6
some_event 事件触发7
some_event 事件触发8
some_event 事件触发9
some_event 事件触发10
what ?

可以发现 what ? 在最后才输出。如果把num设置的非常大就会报错

internal/process/next_tick.js:148
    nextTickQueue.push({
                 ^
RangeError: Maximum call stack size exceeded

V8不断的向事件队列里添加任务,最终导致出现溢出,把event.emit('some_event')改写成

process.nextTick(function(){ 
  event.emit('some_event') 
});

就会发现输出成了

ome_event 事件触发1
what ?
some_event 事件触发2
some_event 事件触发3
some_event 事件触发4
some_event 事件触发5
some_event 事件触发6
some_event 事件触发7
some_event 事件触发8
some_event 事件触发9
some_event 事件触发10

what ?并不会被阻塞,而且无论num改成多少,都不会出现栈溢出的错误。
  Node的Event loop执行流程如图

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
|      nextTick(队列执行)
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
|       nextTick(队列执行)
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      
|      nextTick(队列执行)         ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      |               |
|      nextTick(队列执行)         │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
|       nextTick(队列执行)
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

直接event.emit('some_event')的时候,Node不断的把收集到的事件塞到I/O callbacks这个队列,如果有大量的事件塞入就会最终导致溢出,就是上面的Maximum call stack size exceeded错误。
  如果加了process.nextTick则会不断的把emit的事件回调加到nextTickQueue队列,在各个主队列切换的时候执行,见上图的 nextTick(队列执行)。上面的那段Demo把event.emit('some_event')修改后的执行顺序就是
  1、发送事件
  2、把事件回调函数添加到nextTickQueue(注意,这个时候nextTickQueue队列里只有一个事件回调函数,如果当前队列尚未执行完毕并且没有发生切换,则nextTickQueue队列里的事件永远不会执行)
  3、执行nextTickQueue里的第一个事件回调(当前队列执行完毕或者执行到一定数量发生切换时,事件回调又会重新创建一个新的nextTickQueue队列并添加一个事件回调)
  4、然后同上
  这样就没有阻塞Node的事件循环,无论num多大都不会撑爆I/O callbacks队列。其实最核心的思想就是将任务拆解到若干次事件循环中,逐步执行。

扩展阅读
  Node.js的event loop及timer/setImmediate/nextTick
  Node.js 原理简介
  深入理解Node.js:核心思想与源码分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容