又被 Node 的 Event Loop 坑了,这次是 Node 的锅

摘要: Event Loop 是个大坑啊…

近日在论坛上看到一篇文章讲 node 和谷歌浏览器的 eventloop 的区别,因为看到写的还不错,我表示了肯定。但没过多久一位坛友却说 node11 结果不一样,我说怎么可能不一样。接着坛友贴了个代码,我试着运行了一下,啪啪打脸!

一探究竟

先上被啪啪打脸的代码:

setTimeout(() => {
console.log("timer1");
Promise.resolve().then(function() {
console.log("promise1");
});
}, 0);
setTimeout(() => {
console.log("timer2");
Promise.resolve().then(function() {
console.log("promise2");
});
}, 0);

了解 node 的 eventloop 的同学应该会这样想:

  • 理想情况下这个就是一开始将两个 setTimeout 放进 timers 的阶段。
  • 等到时间到达后运行 timer1,把 promise1 的 Promise 放入 timers 的下一阶段微任务队列中,同理继续运行 timers 的阶段,执行 timer2,把 promise2 的 Promise 放入 timers 的下一阶段微任务队列中。
  • 直到 timers 队列全部执行完,才开始运行微任务队列,也就是 promise1 和 promise2.
    那么如果机器运行良好就是以下结果:
timer1;
timer2;
promise1;
promise2;

node10 运行结果确实是这样,是没问题的。但 node11 运行后居然是:

timer1;
promise1;
timer2;
promise2;

挺吃惊的,但吃惊过后还是仔细去翻 node 的修改日志,在 node 11.0 的修改日志里面发现了这个:

  • Timers
    • Interval timers will be rescheduled even if previous interval threw an error. #20002
    • nextTick queue will be run after each immediate and timer. #22842

然后分别看了 20002 和 22842 的 PR,发现在 #22842 在 lib/timers.js 里面有以下增加:

这两个是什么意思呢?

提示一下 runNextTicks()就是 process._tickCallback()。用过的可能知道这个就是除了处理一些异步钩子,然后就是执行微任务队列的。于是我增加了两 process._tickCallback()在 setTimeout 方法尾部,再使用 node10 运行,效果果然和 node11 一致,代码如下:

setTimeout(() => {
console.log("timer1");
Promise.resolve().then(function() {
console.log("promise1");
});
process._tickCallback(); // 这行是增加的!
}, 0);
setTimeout(() => {
console.log("timer2");
Promise.resolve().then(function() {
console.log("promise2");
});
process._tickCallback(); // 这行是增加的!
}, 0);

那么为什么要这么做呢?

当然是为了和浏览器更加趋同。

了解浏览器的 eventloop 可能就知道,浏览器的宏任务队列执行了一个,就会执行微任务。

简单的说,可以把浏览器的宏任务和 node10 的 timers 比较,就是 node10 只有全部执行了 timers 阶段队列的全部任务才执行微任务队列,而浏览器只要执行了一个宏任务就会执行微任务队列。

现在 node11 在 timer 阶段的 setTimeout,setInterval…和在 check 阶段的 immediate 都在 node11 里面都修改为一旦执行一个阶段里的一个任务就立刻执行微任务队列。

最后

所以在生产环境建议还是不要特意的去利用 node 和浏览器不同的一些特性。即使是 node 和浏览器相同的特性,但规范没确定的一些特性,也建议小心使用。否则一次小小的 node 升级可能就会造成一次线上事故,而不只是啪啪打脸这么简单了。

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有Google、360、金山软件、百姓网等众多品牌企业。欢迎大家免费试用

版权声明

转载时请注明作者 Fundebug以及本文地址:
https://blog.fundebug.com/2019/04/02/nodejs-event-loop-has-changed/

您的用户遇到BUG了吗?

体验Demo 免费使用