尽管 JavaScript 是单线程的,但 Node.js 使用事件循环来执行异步非阻塞 I/O 操作,并尽可能将操作委托给系统内核。大多数现代操作系统内核都是多线程的,能够通过在后台执行它们来处理多个操作。当当前操作完成后,内核会通知Node.js以便可以将适当的回调添加到轮询队列中以最终执行。
事件循环在启动后立即初始化Node.js通过提供.js脚本或在 REPL 模式下进行初始化。事件循环的操作顺序如下图所示 -

- Timers 阶段执行由 setTimeout() 和 setInterval() 调度的回调。
- 挂起的回调阶段执行推迟到下一个循环迭代的 I/O 回调。
- 轮询阶段有两个主要功能:(a) 计算应阻塞和轮询 I/O 的时间长度,以及 (b) 处理轮询队列中的事件。Node.js 在此阶段检索新的 I/O 事件并执行 I/O 相关的回调。
- 检查阶段在轮询阶段完成后立即执行回调。如果轮询阶段变为空闲状态,并且脚本已使用 setImmediate() 计时器排队。事件循环继续进入检查阶段,而不是等待。libuv 库是运行时的一部分Node.js起着为处理异步操作提供支持的作用。

- V8 引擎处理 JavaScript 代码的执行,而 Libuv 库利用各自操作系统的原生机制来实现异步操作。
- 最后,close 回调阶段处理使用 close 事件注册的回调,例如 socket.on('close', function)。如果套接字突然关闭,将发出 close 事件,否则将由 process.nextTick() 方法发出,以将函数的执行推迟到事件循环的下一次迭代。
在开始下一次运行事件循环之前,Node.js检查它是否正在等待任何异步 I/O 或计时器。如果没有,运行时将完全关闭。
了解事件循环的工作原理对于构建可扩展的Node.js应用程序至关重要。事件循环是 Node.js 的基本部分,它通过确保主线程不被阻塞来实现异步编程。