详解nodejs中的事件循环机制


当前第2页 返回上一页

这里面比较关键的是poll阶段:

  • poll队列不为空的时候,事件循环会遍历队列并同步执行回调,直到队列清空或执行回调数达到系统上限。
  • poll队列为空的时候,就会有两种情况:
    • 如果代码中存在setImmediate()回调,那么事件循环直接结束poll阶段进入check阶段来执行check队列里的回调;
    • 如果不存在setImmediate()回调,会等待回调被加入到队列中并立即执行回调,这里同样会有个超时时间设置防止一直等待下去,如果规定时间内有定时器函数进入队列,则返回到timer阶段,执行定时器回调,否则在poll阶段等待回调进入队列。

同样的举个大大的,看看以下代码会输出什么:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

console.log('start')

setTimeout(() => {

  console.log('timer1')

  Promise.resolve().then(function() {

    console.log('promise1')

  })

}, 0)

setTimeout(() => {

  console.log('timer2')

  Promise.resolve().then(function() {

    console.log('promise2')

  })

}, 0)

Promise.resolve().then(function() {

  console.log('promise3')

})

console.log('end')

利用node事件循环分析呗:

  • 先执行同步任务,打印start,end
  • 进入timer阶段前,清空NextTick和micro队列,所以打印promise3
  • 进入timer阶段,打印timer1,并发现有一个微任务,立即执行微任务,打印promise1
  • 仍然在timer阶段,执行下个宏任务,打印timer2,同样遇到微任务,立即执行,打印promise2

因此输出顺序是:start,end,promise3,timer1,promise1,timer2,promise2,如果能正确回答出来说明对node的循环机制有了大体的了解,实际node输出结果确实是这样:

4.png

那如下代码会输出什么呢?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

process.nextTick(function(){

    console.log(7);

});

 

new Promise(function(resolve){

    console.log(3);

    resolve();

    console.log(4);

}).then(function(){

    console.log(5);

});

 

process.nextTick(function(){

    console.log(8);

});

继续分析:

  • process.nextTick会将任务推进至nextTick队列,promise.then会把任务推至micro队列,上面提到过每次一个宏任务执行完,执行下一个宏任务之前需要清空nextTick队列和micro队列,同样的一个阶段执行完,进入下一个阶段之前也需要nextTick队列和micro队列,并且nextTick队列优先级高于micro队列
  • 先执行同步代码,打印3,4
  • 执行nextTick队列,打印7,8
  • 再执行micro队列,打印5

因此最终输出是:3,4,7,8,5,需要记住,process.nextTick 永远大于 promise.then的优先级

还有一个大家很容易混淆的点就是setTimout和setImmediate的执行时机,根据上面描述的node事件循环机制,setImmediate()应该在check阶段执行 与 而setTimeout在timer阶段执行,理论上setTimout比setImmediate先执行,看下面的代码:

1

2

setTimeout(() => console.log(1),0);

setImmediate(() => console.log(2));

执行结果是什么?1,2 还是 2,1,其实都有可能,看实际node运行的结果:

5.png

可以看到两次执行的结果不一样,为什么呢?原因在于即使setTimeout的第二个参数默认为0,但实际上,Node做不到0秒就执行其回调,最少也要4毫秒。那么进入事件循环后,如果没到4毫秒,那么timers阶段就会被跳过,从而进入check阶段执行setImmediate回调,此时输出结果是:2,1;

如果进入事件循环后,超过4毫秒(只是个大概,具体值并不确定),setTimeout的回调会出现在timer阶段的队列里,回调将被执行,之后再进入poll阶段和check阶段,此时输出结果是:1,2

那如果两者在I/O周期内调用,谁先执行呢?看一下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

const fs = require('fs')

 

fs.readFile('./test.txt', 'utf8' , (err, data) => {

  if (err) {

    console.error(err)

    return

  }

  setTimeout(() => {

    console.log('timeout');

  }, 0);

  setImmediate(() => {

    console.log('immediate');

  });

})

实际上,node中输出的结果总是immediate先输出,timeout后输出。因为I/O回调是在poll阶段执行,当回调执行完毕之后队列为空,发现存在setImmediate的回调就会进入check阶段,执行完毕后,再进入timer阶段。

四、总结

本文结合代码示例,对node的事件循环机制做了比较详细描述。通过这篇文章,应该可以了解浏览器的事件循环机制是怎样的,node的循环机制是怎样的,以及nextTick和micro队列的优先级,setTimout和setImmediate执行时机等一些容易混淆的知识点。文章中不足和不对之处,欢迎在评论区交流讨论,一起探索,谢谢。

更多编程相关知识,请访问:编程入门!!

以上就是详解nodejs中的事件循环机制的详细内容,更多文章请关注木庄网络博客

返回前面的内容

相关阅读 >>

浅谈conda安装nodejs版本过低问题的解决方法

使用源码如何编译安装nodejs

一文快速了解node.js中的事件循环

nodejs与javascript的区别

2021年8个值得收藏的nodejs项目

nodejs适合做些什么?

浅谈nodejs中的多线程操作

浅谈nodejs中的?双工流

怎么看nodejs是否安装成功

一文搞懂node.js中的事件循环

更多相关阅读请进入《nodejs》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...