• 你知道Node.js 的的事件循环机制吗?
  • 发布于 4天前
  • 20 热度
    0 评论
  • APAC
  • 18 粉丝 36 篇博客
  •   
前言

js 的事件循环机制可能大家都比较熟悉了,分为宏任务和微任务 。但是 Node.js 的的事件循环机制不知道大家了解多少,这个在面试当中也是比较常问,如果有涉及到 Node.js的 的基本上都会问到事件循环机制,Node.js的事件循环机制和 js 的有所不同,但是大致的理念是相通的,接下来我们就来看一下吧!


核心
Node.js 的事件循环是其异步非阻塞 I/O 模型的核心。它允许 Node.js 在执行非阻塞操作时保持高效,使得单线程的 js 能够处理大量并发操作。
事件循环的基本阶段如下:
Timers( 定时器 ) :执行 setTimeout() 和 setInterval() 设定的回调。
I/O callbacks( I/O回调 ) :处理几乎所有的 I/O 回调,除了 close 回调、setTimers 和 setImmediate() 的回调。
Idle, Prepare( 闲置、准备 ) :系统内部使用,处理一些准备工作。
Poll( 轮询 ) :检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有的 I/O 回调都在这个阶段处理,除了 close 回调,timers 和 setImmediate() 的回调);节点会在这个阶段检查是否有新的定时器到期,如果有,则回到 timers 阶段执行;如果没有新的 I/O 回调,并且当前也没有定时器到期,则会执行 setImmediate() 的回调。
Check( 检查 ) :执行 setImmediate() 设定的回调。
Close Callbacks( 关闭回调 ) :执行关闭事件的回调,如 socket.on('close')。

在 Node.js 中,宏任务和微任务的概念与浏览器环境类似,但有一些特殊性。


宏任务:
setTimeout()
setInterval()
setImmediate()
I/O操作(如fs模块的文件操作)
HTTP请求回调

微任务:
Promise的then()、catch()和finally()回调
process.nextTick()
queueMicrotask()

执行顺序:
1.同步代码
2.微任务
.process.nextTick()队列
.其他微任务队列(Promise等)

宏任务

在每个宏任务执行完毕后,Node.js都会清空微任务队列,然后再进入下一个事件循环阶段。


练习
为了更好的理解,看一段代码
const fs = require('fs')
console.log('开始执行')
Promise.resolve().then(() => {
  console.log('Promise callback')
})
// 堆代码 duidaima.com
// 定时器
setTimeout(() => {
  console.log('定时器回调1')
}, 0)
setTimeout(() => {
  console.log('定时器回调2')
}, 10)

setTimeout(() => {
  console.log('定时器回调3')
}, 100)
// I/O操作
fs.readFile('./template.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('I/O错误:', err)
  } else {
    console.log('I/O回调:', data)
  }
})
// setImmediate
setImmediate(() => {
  console.log('setImmediate回调')
})
// process.nextTick
process.nextTick(() => {
  console.log('process.nextTick回调')
})

console.log('脚本执行结束')
运行结果:
开始执行
脚本执行结束
process.nextTick回调
Promise callback
定时器回调1
setImmediate回调
I/O回调: 我是IO回调
定时器回调2
定时器回调3
解释:
宏任务执行:
"定时器回调1"(setTimeout 0ms)首先执行,这是符合预期的,因为它实际上被设置为1ms延迟。
"setImmediate回调"在"定时器回调1"之后、"I/O回调"之前执行。这说明在这次运行中,事件循环先进入了检查阶段(执行setImmediate),然后才到达了轮询阶段(执行I/O回调)。
"I/O回调"在setImmediate之后执行,这是完全可能的,因为I/O操作的完成时间是不确定的。
"定时器回调2"(setTimeout 10ms)在I/O回调之后执行,这说明10ms的延迟确实比I/O操作和setImmediate的执行时间长。
"定时器回调3"(setTimeout 100ms)最后执行,这是符合预期的,因为它有最长的延迟时间。

关键点:
setImmediate和 I/O 回调的顺序: 在这次运行中,setImmediate回调在 I/O 回调之前执行。这展示了Node.js 事件循环的一个重要特性:检查阶段(执行 setImmediate )可能会在某些 I/O 操作完成之前到来。
定时器的行为: 0ms、10ms 和 100ms 的定时器确实按照预期的顺序执行,但它们的实际执行时间可能会受到其他操作(如I/O和setImmediate)的影响。
I/O操作的不确定性: I/O回调的执行时机证明了异步I/O操作的完成时间是不可预测的,它可能在setImmediate之后,但在较长延迟的定时器之前完成。
用户评论