Node.js事件循环机制是Node.js实现非阻塞I/O 和高并发能力的核心机制,它通过一个单线程的循环来调度和执行各种异步操作的回调函数,使Node.js能够在单线程环境下高效处理大量并发请求。
一、事件循环的基本概念
事件循环(Event Loop)本质上是一个无限循环 ,它不断检查任务队列并执行其中的回调函数。Node.js是单线程的,这意味着它只有一个主线程来执行所有代码,但通过事件循环机制,它可以同时处理多个异步操作,而不会阻塞主线程。
二、事件循环的六个阶段
事件循环按照固定顺序依次执行以下六个阶段,每个阶段都有自己的回调队列(FIFO):
- Timers(计时器阶段) :
- 执行
setTimeout和setInterval设定的回调函数 - 计时器的实际执行时间不保证精确,取决于事件循环其他阶段的执行时间2
- 执行
- Pending callbacks(挂起回调阶段) :
- 执行上一轮循环中被延迟到本轮执行的I/O回调
- 处理系统操作的错误回调,如TCP连接错误
- Idle, prepare(空闲和准备阶段) :
- 仅供Node.js内部使用,通常不涉及用户代码
- Poll(轮询阶段) :
- 事件循环的核心阶段,处理大部分I/O回调(如文件读取、网络请求)
- 检查Poll队列是否为空,决定是否阻塞等待新I/O事件
- 如果Poll队列不为空,会先处理队列中的回调,再检查是否有setImmediate任务5
- Check(检查阶段) :
- 执行
setImmediate设定的回调函数 - setImmediate与setTimeout的区别在于,setImmediate在当前轮询阶段结束后立即执行2
- 执行
- Close callbacks(关闭回调阶段) :
- 执行关闭事件的回调函数,如
socket.on('close', ...)
- 执行关闭事件的回调函数,如
三、宏任务与微任务
事件循环中任务分为两类,执行优先级不同:
1. 宏任务(Macrotasks)
- 定义:在事件循环的各个阶段中执行的任务
- 常见类型 :
setTimeout、setInterval、setImmediate、I/O操作、DOM事件等 - 执行规则:每轮事件循环只执行一个宏任务
2. 微任务(Microtasks)
- 定义:在当前宏任务执行结束后立即执行的任务
- 常见类型 :
process.nextTick、Promise.then/catch/finally、queueMicrotask、MutationObserver - 执行规则 :
- 优先级高于宏任务
- 在当前阶段的回调结束后立即执行
- 一次性清空微任务队列,即使在执行微任务过程中产生新的微任务也会立即执行67
3. 任务执行优先级
- 同步代码(当前执行栈)
- 微任务(当前阶段结束后立即执行)
- 宏任务(按阶段顺序执行)
四、事件循环的执行流程
- 执行同步任务:执行当前调用栈中的同步代码
- 检查微任务队列:清空微任务队列(包括nextTick和Promise)
- 进入事件循环阶段:依次执行Timers → Pending → Poll → Check → Close
- 每个阶段执行后:再次检查并清空微任务队列
- 循环往复:直到所有任务队列为空
五、代码示例:理解执行顺序
console.log('Start');
setTimeout( => {
console.log('setTimeout'); // 宏任务(Timers阶段)
}, 0);
setImmediate( => {
console.log('setImmediate'); // 宏任务(Check阶段)
});
Promise.resolve.then( => {
console.log('Promise'); // 微任务
});
process.nextTick( => {
console.log('nextTick'); // 微任务(优先级最高)
});
console.log('End');
执行输出顺序:
Start End nextTick Promise setTimeout setImmediate
原因分析:
- 同步代码
Start和End先执行 - 微任务
nextTick和Promise在同步代码后立即执行 - 宏任务
setTimeout在Timers阶段执行 - 宏任务
setImmediate在Check阶段执行1
六、Node.js与浏览器事件循环的区别
- 阶段数量:Node.js事件循环有6个阶段,而浏览器通常只有3个阶段(宏任务、微任务、渲染)
- setImmediate:Node.js特有API,浏览器不支持
- nextTick:Node.js特有微任务,浏览器中没有
- I/O处理:Node.js有专门的Poll阶段处理I/O,浏览器没有2
七、实际应用与性能优化
- 避免长时间同步操作:会阻塞事件循环,影响其他任务执行
- 合理使用微任务 :高优先级任务可使用
process.nextTick或Promise - I/O密集型任务:利用Node.js的非阻塞I/O特性,避免使用同步方法
- CPU密集型任务 :考虑使用
worker_threads模块或集群模式分散负载9
Node.js事件循环机制是理解Node.js异步编程模型的关键,掌握其工作原理有助于开发者编写更高效、更可靠的Node.js应用,特别是在处理高并发场景时能够做出更合理的架构设计和性能优化决策。