在node中有两个异步api容易混淆,process.nextTick和setImmediate,这两个一步api都是可以用来异步执行函数的,它们有什么区别呢?
首先说结论,process.nextTick比setImmediate会先执行,且process.nextTick在一轮循环中会执行掉所有的函数,setImmediate一次循环只会执行一个。就像微任务和宏任务的差别。
举个例子:
console.log('out 1');
setImmediate(() => {
console.log('setImmediate 1');
process.nextTick(() => {
console.log('nextTick 4');
process.nextTick(() => {
console.log('nextTick 5');
});
});
});
setImmediate(() => {
console.log('setImmediate 2');
});
process.nextTick(() => {
console.log('nextTick 1');
});
process.nextTick(() => {
console.log('nextTick 2');
process.nextTick(() => {
console.log('nextTick 3');
});
});
console.log('out 2');
以上代码肯定会先执行同步任务,所以首先输出
out1
out2
然后执行process.nextTick,输出
nextTick 1
nextTick 2
在执行第二个process.nextTick的时候,里面又添加了一个process.nextTick,这个也会在setImmediate之前执行,因为在一轮循环中,会先检查所有的process.nextTick执行完了,在去检查setImmediate任务。所以会输出
nextTick 3
接着执行第一个setImmediate,会输出
setImmediate 1
因为一轮循环执行一个setImmediate函数,所以开启第二轮循环,因为在执行第一个setImmediate的时候,又添加了process.nextTick函数,所以第二轮循环会优先执行掉process.nextTick函数,输出
nextTick 4
nextTick 5
然后在执行第二个setImmediate
setImmediate 2
所以最终的执行顺序是
out1
out2
nextTick 1
nextTick 2
nextTick 3
setImmediate 1
nextTick 4
nextTick 5
setImmediate 2
观察者
在node中,所有的异步任务都会有一个观察者,操作系统会询问这个观察者,你这个异步任务执行完了没有,如果执行完了我就执行你对应的回调函数了,如果没执行完,那没关系,我下个循环再来问你一下。
而操作系统询问观察者是有顺序的,观察者分为idle观察者、check观察者和I/O观察者,每一轮循环检查中,询问观察者的顺序是idel观察者 > I/O观察者 > check观察者,而process.nextTick属于idel观察者,setImmediate属于check观察者,所以process.nextTick会先执行。
问题:为什么有了setTimeout还需要process.nextTick和setImmediate?
因为setTimeout是不精确的,我们使用setTimeout(fun, 10)不能保证肯定会在10ms之后执行,且setTimeout的机制和process.nextTick不一样,很消耗性能,而process.nextTick是比较轻量的,这里涉及到node底层实现机制,不再赘述。