主要是setTimeout(另一个定时器setTimeInterval),setImmediate,还有process.nextTick(),执行顺序问题。从其中看node.js事件循环机制。
首先,先看一下node.js的事件循环机制。
或者可以,看看一下下面的详情的例子留个印象或疑惑,再看这里的内容。
官网的图:
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
第一阶段,timer(定时器阶段——也就是这两个setTimeout和setInterval)
1、开始计时
2、执行定时器的回调(注意仅仅是定时器的回调,也就是定时器的回调仅仅在这执行,不在第四阶段)
第二阶段,pending callbacks(系统阶段)理解有这个即可
第三阶段,idle,prepare(准备阶段)理解有这个即可
第四阶段,poll(轮询查询阶段)
1、如果回调队列里有待执行的回调函数(回调队列有很多种事件的回调函数)
从回调队列里取出,同步执行,注意(第一阶段的那两个setTimeout和setInterval的回调不在这里执行),直到回调队列为空(除第一阶段那两个),或者到达系统最大限度(例如内存不允许)。
2、如果回调队列为空(除定时器的回调,其会跳过)
1)如果有设置过setImmediate,则进入下一check阶段来执行去回调。
2)如果没有设置过,则在此阶段停留,等待回调函数插入回调队列。或者定时器到点了,则进入下一check阶段,循环回到第一阶段执行定时器的回调。
第五阶段,check(专门执行setImmediate回调)
第六阶段,close callback(关闭回调阶段)理解有这个即可
还有一个process.nextTick(),这是立即执行的函数。什么是立即执行的函数,也就是无论此时事件循环处于那一阶段,它都可以执行这个函数,且其优先级高,比其他回调先执行。其相当于是一个VIP,在任何阶段都可以执行且首先执行。
详细分析:
setTimeout(() => { console.log('setTimeout'); })
setImmediate(() => { console.log('setImmediate'); })
setTimeout(() => { console.log('setTimeout'); })相当于setTimeout(() => { console.log('setTimeout'); },0)。
可以看到两次结果不一样,为什么两次执行结果会不一样呢?
这是因为当处于第一阶段的时候,这个定时器会交个相应的定时器模块执行,不在主线程执行,有可能这个模块把相应的回调已经执行了放在了回调队列里,而此时事件循环还没有去到下一阶段,所以就随便执行了这个定时器回调。所以会先输出setTimeout。
但也有可能当定时器模块把回调放到回调队列里时,事件循环已经到了下一阶段了,所以此时会先输出setImmediate。
总的来说,这两个谁先,与事件循环的频率有关。
上面是主线程没有内容执行,但如果主线程上有内容要执行,此时定时器模块肯定已经把回调放到了回调队列(因为其设置的定时是0ms),此时肯定会先在第一阶段上执行输出setTimeout。
如下:
setTimeout(() => { console.log('setTimeout'); })
setImmediate(() => { console.log('setImmediate'); })
console.log('主线程的输出');
如果有,nextTick的话,它会在任何一个阶段都可以执行,且先执行(当然还是要先执行主线程的代码先):
setTimeout(() => { console.log('setTimeout'); })
setImmediate(() => { console.log('setImmediate'); })
process.nextTick(() => { console.log('nextTick'); });
console.log('主线程的输出');