nodejs 事件循环

14 篇文章 0 订阅
12 篇文章 0 订阅
本文详细解释了Node.js中的事件循环机制,包括其六个阶段、setTimeout和setInterval的工作原理,以及微任务和Promise的执行顺序。通过实例展示了如何影响程序执行顺序并指出理解这些对解决问题的重要性。
摘要由CSDN通过智能技术生成

浏览器的事件循环比较熟悉了,也来了解下 node 的。
参考来源:

https://nodejs.org/en/guides/event-loop-timers-and-nexttick/
https://juejin.cn/post/6844903999506923528

事件循环分为 6 个阶段,图中每个框都是一个阶段,每个阶段都有一个先进先出的队列来存储要执行的回调。
在这里插入图片描述

每当进入一个阶段,会执行最大数量为 n 个的回调,或者队列回调数少于 n,执行完后进入下一个阶段。

  • timers,执行setTimeout、setInterval 的回调。设定一个下限时间(如果写 0,会被改为 1),之后会执行这些回调,这里可能会被 poll 延迟
  • pending callbacks,执行 操作系统相关的回调,比如 TCP 连接还没连接上就收到了数据,一些操作系统会报错,这就会在这个队列加ECONNREFUSED事件
  • idle, prepare,node内部使用
  • poll,轮询,做两件事
    • 计算因为 I/O要阻塞和轮询多久(计算出来然后呢?)
    • 处理轮询队列里的事件(I/O回调)
    • 如果到了poll 阶段
      • 如果轮询队列不为空,会同步执行最大数量为 n 的回调
      • 如果轮询队列为空,
        • 如果有setImmediate 会进入 check 阶段执行setImmediate的回调
        • 如果 timers到了时间(setTimeout、setInterval 那些),回到 timers 阶段
  • check,setImmediate回调在这里执行
  • close callbacks,一些关闭回调,比如 socket.on(‘close’, …)

了解完事件循环各个阶段,有什么用?可能能避免踩一些坑?虽然之前不了解这个机制,写 node 也没踩过坑🤦‍♂️,万一以后会遇到呢?

setTimeout(()=>{  
    console.log('timeout');
},0);
setImmediate(()=> {
  console.log('immediate');
});

这里似乎直觉上会先出 timeout,但实际 setTimeout 的时间最小是 1。如果机器性能一般,到 timers 阶段已经够 1ms 了,那就先输出 timeout;否则先输出 immediate

再来看这个

var fs = require('fs')

fs.readFile(__filename, () => {
    setTimeout(() => {
        console.log('timeout');
    }, 0);
    setImmediate(() => {
        console.log('immediate');
    });
});

这里是先 immediate 再 timeout,因为是在回调里执行的,就是在 poll 阶段,当队列为空,immediate 优先级更高。

Promise 和process.nextTick 在一个事件循环后执行

再看看这题,看你晕不晕

async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
  }
async function async2(){
    console.log('async2')
}
console.log('script start')
setTimeout(function(){
    console.log('setTimeout0') 
},0)  
setTimeout(function(){
    console.log('setTimeout3') 
},3)  
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick'));
async1();
new Promise(function(resolve){
    console.log('promise1')
    resolve();
    console.log('promise2')
}).then(function(){
    console.log('promise3')
})
console.log('script end')
script start
async1 start
async2
promise1
promise2
script end
nextTick
async1 end
promise3
setTimeout0
setImmediate
setTimeout3

为了方便对照,贴个图片左右对比
在这里插入图片描述

Promise 里的是立即执行的,resolve 和 reject 回调会加到微任务,所以 async1 start 后就会输出 async2。

也因此之后会走到 promise1,promise1 后,把 resolve 回调继续加到微任务,接着就输出 promise2。

之后就走到同步代码的最后 script end。

然后发现微任务队列不为空,最早的就是 nextTick,之后就是 await async2 后面的 async1 end 了。(这里可能疑问为啥都加了 await 了,没有同步执行,因为 async1()前面没加 await)

然后微任务队列还不为空,还有promise3。

之后进入事件循环,这里根据性能影响,最后三个可能会有区别,可能setTimeout3 还在 setImmediate 前面,也可能 setImmediate 在最前面。

好了搞清楚这些有啥用呢?可能是在之后遇到灵异事件时能较快找到原因吧? 顺便满足下好奇心,哈哈。

微任务(microtask)相关

在某个阶段的,一个回调执行完后,会去检查微任务队列,清空微任务队列,如果微任务创建了微任务,也会继续执行。

setTimeout(()=>{
  console.log('timeout1')
  Promise.resolve().then(()=>{
    console.log('microtask 2')
    Promise.resolve().then(()=>{
      console.log('microtask 3')
    })
  })
})
setTimeout(()=>{
  console.log('timeout2')
})
console.log(1)
Promise.resolve().then(()=>{
  console.log('microtask 1')
})
console.log(2)

以上代码输出

1
2
microtask 1
timeout1
microtask 2
microtask 3
timeout2
  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值