事件循环
约 820 字大约 3 分钟
2025-03-15
Node.js 的事件循环(Event Loop)是其异步非阻塞 I/O 操作的核心机制。它允许 Node.js 高效处理大量并发操作,而无需依赖多线程。以下是 Node.js 事件循环机制的详细说明:
一、基本概念
事件循环是一个持续运行的循环,负责处理异步任务的调度和执行。它从事件队列中取出任务,并将其推送到调用栈中执行。事件循环的核心是单线程,但通过异步 I/O 和非阻塞操作,Node.js 能够高效处理并发请求。
二、运行阶段
Node.js 的事件循环分为多个阶段,每个阶段都有特定的任务队列。以下是事件循环的主要阶段:
1、Timers 阶段
- 处理
setTimeout
和setInterval
的回调。 - 检查定时器是否到期,到期则执行回调。
2、Pending Callbacks 阶段
- 执行一些系统操作的回调,例如 TCP 错误回调。
3、Idle 阶段
- 内部使用的阶段,通常不需要关注。
4、Prepare 阶段
- 内部使用的阶段,通常不需要关注。
5、Poll 阶段
- 检索新的 I/O 事件(如文件读取、网络请求等)。
- 执行与 I/O 相关的回调。
- 如果 Poll 队列为空:
- 如果队列为空,检查是否有
setImmediate
回调:- 有:进入 Check 阶段。
- 无:等待新的 I/O 事件,阻塞时间由最近的定时器到期时间决定
- 如果队列为空,检查是否有
6、Check 阶段
- 执行
setImmediate
的回调。
7、Close Callbacks 阶段
- 执行一些关闭事件的回调,例如
socket.on('close', ...)
。
执行顺序
- 执行同步代码。
- 将异步任务(如
setTimeout
、setImmediate
、I/O 操作)分配到对应队列。 - 事件循环按照阶段顺序处理任务:
- Timers → Pending Callbacks → Poll → Check → Close Callbacks。
- 每个阶段执行完毕后,事件循环会检查是否有新的任务,如果有则继续循环。
三、关键 API 的归属阶段
API | 阶段/类型 | 说明 |
---|---|---|
setTimeout | Timers 阶段 | 回调在 Timers 阶段执行。 |
setImmediate | Check 阶段 | 回调在 Check 阶段执行。 |
fs.readFile | Poll 阶段 | I/O 完成后回调加入 Poll 队列。 |
process.nextTick | 微任务队列 | 当前阶段结束后立即执行。 |
Promise.then() | 微任务队列 | 优先级次于 process.nextTick 。 |
四、微任务(Microtasks)和宏任务(Macrotasks)
- 微任务:包括
Promise
的回调、process.nextTick
。- 微任务在当前阶段结束后、下一个阶段开始前执行。在 每个阶段切换时(即每个宏任务阶段完成后),会清空微任务队列。
process.nextTick
的优先级高于Promise
。
- 宏任务:包括
setTimeout
、setInterval
、setImmediate
、I/O 操作。- 宏任务在事件循环的各个阶段中执行。
五、示例代码
console.log("Start");
setTimeout(() => {
console.log("Timeout");
}, 0);
setImmediate(() => {
console.log("Immediate");
});
Promise.resolve().then(() => {
console.log("Promise");
});
process.nextTick(() => {
console.log("Next Tick");
});
console.log("End");
输出顺序:
Start
End
Next Tick
Promise
Timeout
Immediate
六、总结
- Node.js 的事件循环是单线程的,但通过异步和非阻塞 I/O 实现高并发。
- 事件循环分为多个阶段,每个阶段处理不同类型的任务。
- 微任务(如
Promise
、process.nextTick
)优先于宏任务(如setTimeout
、setImmediate
)执行。
理解事件循环机制对于编写高效的 Node.js 代码至关重要,尤其是在处理异步操作时。
更新日志
2025/8/24 08:17
查看所有更新日志
e7112
-1于