process.nextTick和setImmediate的区别

952 阅读2分钟

在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底层实现机制,不再赘述。