Node.js 事件循环
事件循环是 Node.js 处理非阻塞 I/O 操作的核心机制,使得单线程能够高效处理多个并发请求。
Node.js 是基于单线程的 JavaScript 运行时,利用事件循环来处理异步操作,如文件读取、网络请求和数据库查询。
事件循环使得 Node.js 能够非阻塞地运行代码、处理多个连接、以及执行异步 I/O 操作。
事件循环使得 Node.js 能够处理大量并发的 I/O 操作而不会导致线程阻塞,这是 Node.js 高效处理并发请求的关键。
事件循环的阶段
事件循环分为多个阶段,每个阶段处理特定的任务。关键阶段如下:
- Timers:执行
setTimeout()和setInterval()的回调。 - I/O Callbacks:处理一些延迟的 I/O 回调。
- Idle, prepare:内部使用,不常见。
- Poll:检索新的 I/O 事件,执行与 I/O 相关的回调。
- Check:执行
setImmediate()回调。 - Close Callbacks:处理关闭的回调,如
socket.on('close', ...)。
事件循环的流程
- 任务进入事件循环队列。
- 事件循环按照阶段顺序进行处理,每个阶段有自己的回调队列。
- 事件循环会在
poll阶段等待新的事件到达,如果没有事件,会检查其他阶段的回调。 - 如果
setImmediate()和setTimeout()都存在,setImmediate()在check阶段先执行,而setTimeout()在timers阶段执行。
示例代码
setTimeout(() => {
console.log('Timeout callback');
}, 0);
setImmediate(() => {
console.log('Immediate callback');
});
console.log('Main thread execution');
输出顺序:
Main thread execution先打印。setImmediate()和setTimeout()的执行顺序取决于当前事件循环的状态,一般setImmediate()会先执行。
宏任务与微任务
- 宏任务:
setTimeout、setInterval、setImmediate、I/O 操作等。 - 微任务:
process.nextTick、Promise.then。
执行顺序:微任务优先级高于宏任务,会在当前阶段的回调结束后立即执行。
示例代码
setTimeout(() => {
console.log('Timeout callback');
}, 0);
Promise.resolve().then(() => {
console.log('Promise callback');
});
console.log('Main thread execution');
执行输出结果:
Main thread execution Promise callback Timeout callback
process.nextTick()
process.nextTick() 会在当前操作结束后、下一个阶段开始前执行微任务,优先级高于 Promise。
示例代码
process.nextTick(() => {
console.log('Next tick callback');
});
console.log('Main thread execution');
输出:
Main thread execution Next tick callback
事件驱动程序
在 Node.js 中,事件驱动编程主要通过 EventEmitter 类来实现。
EventEmitter 是一个内置类,位于 events 模块中,通过继承 EventEmitter,你可以创建自己的事件发射器,并注册和触发事件。
通过这种机制,Node.js 可以高效地处理异步任务,即使在单线程的环境下也能实现并发处理。