异步操作的本质是让程序不因等待I/O而阻塞,事件循环作为单线程调度中心,将I/O委托libuv交由操作系统异步处理,完成后通过回调通知机制派发任务;微任务优先于宏任务执行,实现CPU与I/O的错峰并行利用。

异步操作的本质,是让程序不因等待 I/O(比如读文件、发请求)而停住,而是把“等结果”这件事交给系统去处理,自己先干别的事。事件循环就是 Node.js 实现这个逻辑的调度中心——它不主动干活,但一直盯着哪些事该做了、谁的结果回来了,然后按规则把回调函数安排上。
事件循环不是“多线程”,而是一个单线程的调度器
Node.js 是单线程的,事件循环本身不执行 I/O,也不做计算,它只负责轮询和派发。真正耗时的 I/O 操作(如磁盘读写、网络收发)由底层 libuv 库委托给操作系统完成。操作系统做完后,会把结果和对应的回调放进队列,事件循环再从队列里取出执行。
- 同步代码(如 console.log、变量赋值)直接在主线程跑,立刻执行
- 异步 I/O 调用(如 fs.readFile、fetch)一发起就返回,不卡主线程
- 回调函数不会立刻执行,而是等 I/O 完成、被放入队列、轮到它时才运行
非阻塞 I/O 的关键:操作系统 + 回调通知机制
非阻塞不是“不等”,而是“不傻等”。Node.js 发起 I/O 请求后,立即返回控制权;操作系统在后台异步处理,完成后通过事件通知(如 epoll、kqueue)唤醒 Node.js,触发回调。整个过程没有线程挂起或忙等,CPU 不空转。
- 阻塞式:fs.readFileSync() → 线程卡住,直到文件读完才继续
- 非阻塞式:fs.readFile() → 立即返回,后续靠回调拿数据
- 底层依赖:Linux 用 epoll,macOS 用 kqueue,Windows 用 IOCP,libuv 统一封装
事件循环分阶段执行,微任务优先于宏任务
事件循环不是简单地“有任务就执行”,而是按固定顺序遍历多个阶段,每个阶段处理特定类型的回调。其中 Promise.then() 属于微任务(microtask),总在当前阶段结束前清空;setTimeout、setInterval 属于宏任务(macrotask),要等到下一轮循环。
- 计时器阶段:执行已到期的 setTimeout/setInterval
- 轮询阶段:处理 I/O 回调(如文件读完、HTTP 响应到达)
- 检查阶段:执行 setImmediate(Node.js 特有)
- 关闭阶段:处理 socket.close() 等清理逻辑
- 微任务队列(Promise、queueMicrotask)在每个阶段结束后立即清空
为什么异步能提升性能?核心在资源错峰利用
CPU 运算快,I/O 操作慢(尤其磁盘、网络)。同步模式下,CPU 大量时间在空等;异步模式下,CPU 在等 I/O 时可以处理其他请求、响应用户输入、执行计算逻辑——相当于让 CPU 和 I/O 设备并行工作,而不是串行排队。
- 一个 HTTP 服务器同时处理 1000 个连接,同步需 1000 个线程,异步只需 1 个线程 + 事件循环
- I/O 密集型场景(如 API 网关、日志收集)中,异步吞吐量远高于同步
- 注意:CPU 密集型任务(如大数组排序)不适合异步,反而可能因上下文切换降低效率


















