本文是我之前关于 Node.js 事件循环的系列文章的简短跟进。在之前的系列文章中,我详细讨论了Timers,setImmediate,process.nextTick,Promises等等。
但是,自 Node.js v11.0.0 起,对 setTimeout,setImmediate,process.nextTick 和Promises 的行为进行了一些重大更改。在本文中,我将讨论这些新的更改以及 Node < v11.0.0 和 Node ≥ v11.0.0 之间的功能的一些比较。如果您错过了我之前关于 Node.js事件循环的任何文章,我建议您从以下链接中阅读它们然后返回此处以查看 Node v11.0.0中引入的新更改。
如果您分别在浏览器和 Node.js 中单独运行以下代码,会得到完全相反的结果。
setTimeout(() => console.log('timeout1'));
setTimeout(() => {
console.log('timeout2')
Promise.resolve().then(() => console.log('promise resolve'))
});
setTimeout(() => console.log('timeout3'));
setTimeout(() => console.log('timeout4'));
在浏览器中,会输出如下结果:
timeout1
timeout2
promise resolve
timeout3
timeout4
然而在低于v11.0.0版本的 Node.js 中,会输出以下结果:
timeout1
timeout2
timeout3
timeout4
promise resolve
在 Node.js 的实现中,当程序横跨 C ++ / JavaScript 边界时,会在事件循环( event loop )的每个阶段之间执行 process.nextTick 回调和微任务 ( microtasks, 例如,promise回调 )。因此,在执行 Promise 的回调之前,所有定时器的回调都会在事件循环( event loop )的定时器阶段执行,从而产生上述输出。
然而,浏览器和Node之间的这种矛盾的输出已经被讨论了很长一段时间了,并且一个特性(或bug修复)已经登陆 Node.js v11.0.0 用以跟踪浏览器行为。使用此功能,Node.js v11.0.0或更高版本将输出与浏览器输出匹配的以下内容:
timeout1
timeout2
promise resolve
timeout3
timeout4
请参阅以下 Node v10.15.1 和 Node v11.10.0 之间的比较:
Different outputs in Node v10 and v11
此更改不仅会影响到 setTimeout,还会影响 setImmediate。让我们尝试在 Node v10 和Node v11 中运行以下代码,看看输出是怎样的:
setImmediate(() => console.log('immediate1'));
setImmediate(() => {
console.log('immediate2')
Promise.resolve().then(() => console.log('promise resolve'))
});
setImmediate(() => console.log('immediate3'));
setImmediate(() => console.log('immediate4'));
Node.js v10 和 Node.js v11 明确地给出了两个不同的输出:
如果您替换 Promise.resolve()然后使用 process.nextTick,则此结果完全相同,因为微任务是在运行 process.nextTick 回调之后才会运行。让我们尝试运行以下代码段:
setImmediate(() => console.log('timeout1'));
setImmediate(() => {
console.log('timeout2')
process.nextTick(() => console.log('next tick'))
});
setImmediate(() => console.log('timeout3'));
setImmediate(() => console.log('timeout4'));
上述脚本在 Node.js v10 和 Node.js v11 中的输出如下:
随着 Node v11 的新改动,nextTick 回调 和 微任务( microtasks )将在每个单独的 setTimeout 和setImmediate 回调之间运行,即使定时器队列或 immediates 队列不为空。就setTimeout 和 Promise 回调而言,Node.js v11中的新改动与浏览器行为相匹配,从而提高了 Node.js 中浏览器JavaScript的可重用性。但是,这一重大变化可能会破坏明确依赖旧行为的现有的 Node.js 应用程序。因此,如果要升级到Node v11或更高版本(最好是下一个LTS v12),您可能需要谨慎考虑。