node.js中的setTimeout,setImmediate,Promise,process.nextTick

  目录

nodejs中的事件循环宏任务和微任务

宏任务

首先,看一下nodejs的任务队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
┌───────────────────────┐

┌> │timers │执行setTimeout() 和 setInterval()中到期的callback

│ └──────────┬────────────┘

│ ┌──────────┴────────────┐

│ │pending callbacks│执行系统操作的回调, 如:tcp, udp通信的错误callback

│ └──────────┬────────────┘

│ ┌──────────┴────────────┐

│ │idle, prepare │只在内部使用

│ └──────────┬────────────┘

│ ┌──────────┴────────────┐

│ │poll │执行与I/O相关的回调

│ (除了close回调、定时器回调和setImmediate()之外,几乎所有回调都执行);

│ └──────────┬────────────┘

│ ┌──────────┴────────────┐

│ │check │执行setImmediate的callback

│ └──────────┬────────────┘

│ ┌──────────┴────────────┐

└─┤close callbacks │执行close事件的callback,例如socket.on("close",func)

└───────────────────────┘
————————————————

nodejs任务队列有timers,pending callbacks,idle, prepare,poll ,check,close callbacks这6个阶段,其中第2,3个属于nodejs内部的不去探讨,还剩下1,4,5,6.
先看两个宏任务的执行顺序:

1
2
3
4
5
6
7
setTimeout(()=> {
console.log('setTimeout');
}, 0);

setImmediate(()=> {
console.log('setImmediate');
});

输出的结果不是固定的,可能是nodejs启动时进入的任务队列时机不一样造成的,再看下边的例子:

1
2
3
4
5
6
7
8
9
fs.readFile('./file.txt', 'utf-8', (err, data)=> {
setTimeout(()=> {
console.log('setTimeout');
}, 0);

setImmediate(()=> {
console.log('setImmediate');
});
});

这个例子的输出就是固定的,永远都是”setImmediate”,”setTimeout”,因为按照按照上边任务队列的表来看,poll之后就是check。

微任务

Promise和process.nextTick是两个微任务,他们不在任务队列的表格中。他们的执行跟在浏览器端一样,在宏任务之前执行,就是每个宏任务执行之后都会把微任务都执行完了再去执行宏任务。

1
2
3
4
5
6
7
Promise.resolve().then(()=> {
console.log('Promise');
});

process.nextTick(()=> {
console.log('nextTick');
});

nextTick优先顺序高于Promise

总结

最后写一个宏任务和微任务都有的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
setTimeout(()=> {
console.log('setTimeout');
process.nextTick(()=> {
console.log('setTimeout-nextTick');
});
}, 0);

setImmediate(()=> {
console.log('setImmediate');
process.nextTick(()=> {
console.log('setImmediate-nextTick');
});
});

Promise.resolve().then(()=> {
console.log('Promise');
});

process.nextTick(()=> {
console.log('nextTick');
});

执行后发现,当有下边两个微任务在的时候,setTimeout永远在setImmediate前执行,当没有这两个微任务的时候,先后顺序又不一定了,侧面说明微任务可以校准任务队列的执行时机。