WdBly Blog

懂事、有趣、保持理智

WdBly Blog

懂事、有趣、保持理智

周维 | Jim

603927378@qq.com

Node中的Event Loop

node.js的任务运行机制、Event Loop,和浏览器环境的对比

在上一篇文章中我们分析了浏览器环境下的Event Loop的运行机制,在这篇文章中我们一起来学习Node中的事件循环,看看和浏览器端的有所不同。

单线程

node也是单线程的执行环境和javascript相同

任务队列

对于异步任务,node同样使用任务队列来处理。

宏任务和微任务

node中的宏任务主要有四类:

  1. timer callback setTimeout和setInterval的回调
  2. IO callback IO回调
  3. check callback setImmediate的回调
  4. close callback 关闭任务的回调如:http.on(“close”, …)

微任务:

  1. Next Tick process.nextTick的回调
  2. Promise Promise的回调

node执行任务队列中的任务流程如下:
image.png

node首先执行全局代码,并向任务队列中添加异步任务 -> 按照Next Tick、Promise的顺序执行完所有微任务 -> 按照顺序执行一类宏任务 -> 微任务 -> 另一类宏任务 -> …

和浏览器端的Event Loop对比, 不同点有:

  • 执行微任务时是按照微任务顺序执行的
  • 执行宏任务时是按照宏任务执行顺序,一次执行完一类宏任务

练习

先自己试试看吧

setTimeout(() => { console.log(2); setTimeout(() => { console.log(3); }, 0); setImmediate(() => { console.log(4); }) process.nextTick(() => { console.log(5); setTimeout(() => { console.log(6); }, 0) }) }, 0); setImmediate(() => { console.log(7); process.nextTick(() => { console.log(8); }) }) new Promise(resolve => { console.log(9); resolve(); }).then(() => { console.log(10); }) setTimeout(() => { console.log(11); process.nextTick(() => { console.log(12); }) }, 0); process.nextTick(() => { console.log(13); Promise.resolve().then(() => { console.log(14) }); }) console.log(15);

输出结果是:

1、9、15、 13、10、14、 2、11、 5、12、 7、4、 8、 3、6

分析:

  • 执行全局代码: 1、9、15,注意promise的handle中代码是同步的,所有先输出9。
  • 执行微任务,先nextTick后Promise: 13、10、14
  • 执行完队列中所有timer任务: 2、11,这里注意了,此时任务队列中只有输出2和11的timer任务存在,其他timer任务还没有进入任务队列。
  • 执行微任务: 5、12
  • 执行所有setImmediate任务: 7、4
  • 执行微任务: 8

到这里node的一个Event Loop已经完成了, 此时node执行栈又会去队列中按照顺序询问宏任务并执行

  • 执行timer宏任务: 3、6

上述答案都是推测的,在实际测试中出现了一些问题, 第一次执行没有问题,输出与我们的推测一致, 但再次执行时遇到了不一致,输出如下:

1、9、15、 13、10、14、 2、11、 5、12、 3、7、4、8、6

测试发现,当执行微任务输出5、12后,本应该执行setImmediate任务先输出7、4,但是确输出了3,然后继续执行发现没有微任务,然后才执行的setImmediate任务输出7、4

最后查看文档得知,在node主程序中执行setImmediate与setTimeout(fn,0)的顺序是随机的无法确定的。所以很难受

总结

  • node的执行环境同Javascript都是单线程
  • node的微任务是按照先nextTick后Promise的顺序执行的
  • node每次都会将任务队列中的同一类宏任务全部执行
  • 在微任务中添加微任务, 新添加的微任务会添加到任务队列末尾,在本个周期执行
  • 新添加的宏任务会在下个周期执行,如在setTimeout中添加的setTimeout任务会在下一个Event Loop中执行
  • 规则如上,但在主线程中的setImmediate与setTimeout(fn,0)的顺序无法确定。