Node.js的事件循环是其实现非阻塞I/O的核心机制,整个循环被划分为多个阶段,每个阶段按照固定顺序执行,处理不同类型的异步任务。pending阶段是事件循环中比较特殊的一个阶段,主要负责处理之前某些操作遗留的待处理回调。

pending阶段的核心作用
pending阶段的主要功能是执行那些由操作系统层面触发的、之前阶段没有处理完成的I/O相关回调。比如在使用TCP服务器时,如果之前的轮询阶段收到了新的连接请求,但是相关的回调没有在对应阶段执行完,这些回调就会被放到pending阶段等待处理。简单来说,pending阶段是一个“补漏”的环节,处理那些因为各种原因没有在对应专属阶段执行的I/O回调。
pending阶段的触发场景
常见的会触发pending阶段处理的场景主要有以下几类:
- TCP服务器的连接回调:当TCP服务器接收到新的客户端连接时,相关的连接回调如果没有在poll阶段处理,就会进入pending阶段
- 一些底层I/O操作完成后的遗留回调:部分操作系统层面的I/O操作完成后,回调可能不会被立即放到对应阶段,而是进入pending队列等待处理
pending阶段和其他阶段的区别
为了更清晰地理解pending阶段的定位,我们可以把它和事件循环的其他主要阶段做对比:
| 阶段名称 | 处理任务类型 |
|---|---|
| timers阶段 | 处理setTimeout、setInterval的到期回调 |
| pending阶段 | 处理之前遗留的I/O相关待处理回调 |
| poll阶段 | 处理新的I/O事件,执行I/O相关回调 |
| check阶段 | 处理setImmediate的回调 |
代码示例验证pending阶段执行逻辑
我们可以通过一个简单的TCP服务器示例来观察pending阶段的相关表现,以下代码创建了一个简单的TCP服务器,监听客户端连接:
const net = require('net');
// 创建TCP服务器
const server = net.createServer((socket) => {
console.log('收到新的客户端连接');
socket.end('hello from server');
});
// 启动服务器监听8124端口
server.listen(8124, () => {
console.log('服务器启动,监听8124端口');
});
// 模拟一个定时器,观察执行顺序
setTimeout(() => {
console.log('timers阶段回调执行');
}, 0);当我们运行这段代码后,服务器的启动回调会在事件循环的合适阶段执行,如果有客户端连接到8124端口,连接相关的回调如果在之前的阶段没有处理完,就会进入pending阶段执行。而setTimeout的回调会在timers阶段执行,通过打印的顺序可以大致判断不同阶段的执行顺序。
注意事项
需要注意的是,pending阶段的执行并不是每次事件循环都会触发,只有当存在待处理的遗留I/O回调时,才会进入这个阶段执行相关任务。如果当前循环中没有需要处理的pending回调,事件循环会直接跳过这个阶段,进入下一个阶段。开发者不需要手动干预pending阶段的执行,Node.js会自动处理这个阶段的逻辑,了解它的作用主要是为了更好地理解异步任务的执行顺序,排查相关的执行时序问题。