废话不多说,直接上代码:
function block(ms) {
let start = Date.now();
while (Date.now() - start < ms) {}
}
setTimeout(() => {
console.log(1);
setImmediate(() => {
console.log(2);
});
new Promise((resolve) => {
console.log(3);
resolve();
}).then(() => {
console.log(4);
process.nextTick(() => {
console.log(5);
});
});
process.nextTick(() => {
console.log(6);
Promise.resolve().then(() => {
console.log(7);
});
});
});
setImmediate(() => {
console.log(8);
});
setTimeout(() => {
console.log(9);
process.nextTick(() => {
console.log(10);
});
});
block(10);
解析
Node Event Loop 的 6 个阶段
- timers: this phase executes callbacks scheduled by setTimeout() and setInterval().
- pending callbacks: executes I/O callbacks deferred to the next loop iteration.
- idle, prepare: only used internally.
- poll: retrieve new I/O events; execute I/O related callbacks (almost all with the exception of close callbacks, the ones scheduled by timers, and setImmediate()); node will block here when appropriate.
- check: setImmediate() callbacks are invoked here.
- close callbacks: some close callbacks, e.g. socket.on('close', ...). 摘自 Node.js 官网文章
过程太多不好理解,按我自己的理解总结:
- Timer Phase:同步代码执行完毕,进入 Event Loop,首先会检查是否有到期的 Timer,执行完所有 timer callback
- I/O Phase:执行所有的 I/O callback
- Polling:如果 Immediates 队列没有任务,就一直等待直到有新的 I/O 完成,或者到达系统设定的最大等待时间;如果有 Immediate 任务就直接进入下一阶段
- Immediate Phase:执行所有 Immediate callback
- close callback:执行所有 close callback
配合下图理解更佳:
nextTick 队列和 Promise 队列
- 两个队列都是在每完成 一个宏任务 之后执行(Node v11 之后)
- nextTick 队列 优先 于 Promise 队列
- 必须 清空 一个队列的所有任务才能转向另一个队列
小练习:
Promise.resolve().then(() => {
console.log(1);
process.nextTick(() => {
console.log(2);
});
Promise.resolve().then(() => {
console.log(3);
Promise.resolve().then(() => {
console.log(7);
});
});
});
process.nextTick(() => {
console.log(4);
process.nextTick(() => {
console.log(5);
});
Promise.resolve().then(() => {
console.log(6);
});
});
答案:4 5 1 6 3 7 2
Promise 原理
你只需要知道在 new Promise(executor)
里的 executor 是同步的,而 .then(callback)
是异步的即可。这部分不了解的自行搜索 “Promise原理”
附带我的 Promise 实现代码:MarvinXu/understanding-promise: An easy-to-understand version of Promise/A+ implementation
setTimeout 和 setImmediate
下面的代码输出结果并不确定,因为 Node.js 中的 timer 延时最小为 1ms,有可能在 1ms 后事件循环已经过了 timer phase,而先输出 immediate
,反之亦然
setTimeout(() => {
console.log("timeout");
}, 0);
setImmediate(() => {
console.log("immediate");
});
// 先后不确定
所以我在开始的题目中加了 block(10)
,是为了保证进入事件循环时 timer 已经到期
我的解题方法
脑子里按行去执行代码,把对应 callback 加入队列,然后依次去清空每个队列,即得到最终结果
timeout: 1 9
immediate: 8
nextTick:
promise:
output:
答案
1 3 6 4 7 5 9 10 8 2