Node.js 中的阻塞和非阻塞

2025年3月5日 | 阅读 4 分钟

在本文中,我们将讨论 Node.js 中带有示例的阻塞非阻塞操作。

什么是阻塞操作?

JavaScript 代码会阻塞操作,直到它们完成,否则不允许其他代码运行。换句话说,阻塞操作会占用整个线程直到完成。如果操作,例如文件 I/O 操作或网络请求,需要很长时间才能完成,这可能会导致严重的延迟。

示例

请看下面 Node.js 中阻塞文件读取操作的示例

输出

Blocking and Non-Blocking in Node.js

说明

在此示例中,使用的方法是同步(阻塞)方法fs.readFileSync。这是最后一行代码,程序将在读取文件之前停止并等待文件完全读取。在服务器环境中一次处理多个请求时,这可能会带来麻烦。

什么是无阻塞操作?

然而,由于非阻塞操作,代码仍可在操作进行时执行。Node.js 中的异步 API,通常使用回调、Promise 或 async/await,用于执行非阻塞活动。

示例

下面是一个不阻塞的文件读取操作的示例

输出

Blocking and Non-Blocking in Node.js

说明

在此示例中,fs.readFile 方法使用了异步(非阻塞)方法。程序在开始文件读取过程后,会直接跳转到下一行代码。一旦文件被完全读取,就会触发回调函数。它消除了等待文件读取的需要,这极大地提高了应用程序的响应能力和性能。

事件循环

Node.js 的事件循环和事件驱动架构支持非阻塞进程。当合适的时候,Node.js 可以通过将工作外包给系统内核来执行非阻塞操作,这得益于事件循环。

当启动异步操作时,Node.js 会注册一个回调并执行剩余的代码。事件循环会在操作完成后捕获回调并执行。正因为如此,Node.js 才能同时处理多个任务,而不会出现代码执行问题。

Async/Await 和 Promise

Node.js 经常使用 Promise 回调和 async/await 语法来管理非阻塞操作。这些策略中的每一种都能有效地处理异步进程。

1. 回调 (Callbacks)

回调是作为参数调用的函数。在另一个函数执行完成后,它们会被调用。

输出

Blocking and Non-Blocking in Node.js

2. Promise

Promise 是一个尚未完成的操作。然而,预计它将在未来完成。使用 Promise 可以避免回调地狱。可以更整洁地处理异步活动。这是通过 Promise 实现的。

输出

Blocking and Non-Blocking in Node.js

3. 异步 (Async/Await)

通过使用在 ES2017 中引入的 Async/Await 语法,程序员可以编写看起来像同步的异步代码。它使代码更易于理解和维护。

输出

Blocking and Non-Blocking in Node.js

实际考虑

为了编写有效的 Node.js 应用程序,我们必须理解阻塞和非阻塞活动。以下是一些有用的建议,供您参考:

  • 避免阻塞代码: 在代码中,尤其是在服务器应用程序中,要避免同步文件 I/O 等阻塞活动。阻塞活动通过阻止其他进程运行,可能会严重降低性能。
  • 利用异步 API: 在可能的情况下,请使用异步 API。Node.js 的几乎所有内置模块(包括文件系统和网络请求操作)都有异步版本。
  • 使用 Async/Await 和 Promise: 使用 Promise 和 async/await 语法是一种更简单、更简洁的处理异步任务的方法。它们使代码更易于阅读。它们有助于维护并避免回调地狱。
  • 了解事件循环: 编写更有效的代码是可能的。对事件循环如何处理异步进程有深刻的理解至关重要。确保代码不会因为执行耗时操作而阻塞事件循环。
  • 优化性能: 要找到应用程序中的瓶颈,请始终对其进行剖析和测试。要衡量我们代码的某些部分在多大程度上运行良好,请使用 Node.js 内置的 console.time 和 console.timeEnd 等工具。

结论

总之,Node.js 中的阻塞和非阻塞进程是影响应用程序可伸缩性和性能的基础概念。阻塞活动会停止代码执行,这可能导致性能问题,尤其是在并发环境中。Node.js 可以同时处理多个活动,这由于事件循环和异步 API 提供的非阻塞操作而提高了响应能力和效率。