Q2: 解释 Node.js 的事件循环(Event Loop)机制。它由哪些阶段组成?

17 阅读2分钟

Node.js 面试题详细答案 - Q2

Q2: 解释 Node.js 的事件循环(Event Loop)机制。它由哪些阶段组成?

事件循环概述

事件循环是 Node.js 的核心机制,负责执行代码、收集和处理事件,以及执行队列中的子任务。

事件循环的六个阶段

// 事件循环的六个阶段
// 1. timers          - 执行 setTimeout 和 setInterval 回调
// 2. pending callbacks - 执行 I/O 回调
// 3. idle, prepare   - 内部使用
// 4. poll            - 获取新的 I/O 事件
// 5. check           - 执行 setImmediate 回调
// 6. close callbacks - 执行 close 事件回调

详细阶段说明

1. Timers 阶段

执行 setTimeoutsetInterval 的回调函数。

console.log('开始')

setTimeout(() => {
  console.log('Timer 1')
}, 0)

setTimeout(() => {
  console.log('Timer 2')
}, 0)

console.log('结束')

// 输出:
// 开始
// 结束
// Timer 1
// Timer 2
2. Pending Callbacks 阶段

执行延迟到下一个循环迭代的 I/O 回调。

const fs = require('fs')

fs.readFile('file.txt', (err, data) => {
  console.log('文件读取完成')
})
3. Poll 阶段

获取新的 I/O 事件,执行相关的回调函数。

// Poll 阶段会检查是否有新的 I/O 事件
// 如果有,立即执行回调
// 如果没有,会等待新的 I/O 事件
4. Check 阶段

执行 setImmediate 的回调函数。

console.log('开始')

setImmediate(() => {
  console.log('setImmediate 回调')
})

console.log('结束')

// 输出:
// 开始
// 结束
// setImmediate 回调
5. Close Callbacks 阶段

执行 close 事件的回调函数。

const server = require('http').createServer()

server.on('close', () => {
  console.log('服务器关闭')
})

server.close()

微任务队列

在事件循环的每个阶段之间,会处理微任务队列:

console.log('开始')

setTimeout(() => {
  console.log('setTimeout')
}, 0)

Promise.resolve().then(() => {
  console.log('Promise')
})

setImmediate(() => {
  console.log('setImmediate')
})

console.log('结束')

// 输出:
// 开始
// 结束
// Promise (微任务优先)
// setTimeout
// setImmediate

完整的事件循环示例

const fs = require('fs')

console.log('1. 同步代码开始')

setTimeout(() => {
  console.log('2. setTimeout 回调')
}, 0)

setImmediate(() => {
  console.log('3. setImmediate 回调')
})

process.nextTick(() => {
  console.log('4. process.nextTick 回调')
})

Promise.resolve().then(() => {
  console.log('5. Promise 回调')
})

fs.readFile(__filename, () => {
  console.log('6. 文件读取回调')

  setTimeout(() => {
    console.log('7. 文件读取中的 setTimeout')
  }, 0)

  setImmediate(() => {
    console.log('8. 文件读取中的 setImmediate')
  })
})

console.log('9. 同步代码结束')

// 输出顺序:
// 1. 同步代码开始
// 9. 同步代码结束
// 4. process.nextTick 回调
// 5. Promise 回调
// 2. setTimeout 回调
// 3. setImmediate 回调
// 6. 文件读取回调
// 8. 文件读取中的 setImmediate
// 7. 文件读取中的 setTimeout

优先级总结

  1. 同步代码 - 最高优先级
  2. process.nextTick - 微任务,优先级最高
  3. Promise - 微任务
  4. setImmediate - 宏任务
  5. setTimeout/setInterval - 宏任务

实际应用

// 利用事件循环优化性能
function heavyTask() {
  return new Promise((resolve) => {
    // 将重任务分解为小块
    function processChunk() {
      // 处理一小块数据
      if (/* 还有数据 */) {
        setImmediate(processChunk); // 让出控制权
      } else {
        resolve();
      }
    }
    processChunk();
  });
}

总结

事件循环是 Node.js 实现非阻塞 I/O 的核心机制,通过六个阶段的循环执行,配合微任务队列,实现了高效的异步编程模型。理解事件循环对于编写高性能的 Node.js 应用至关重要。