Node.js 中的 Async/Await

2025年5月1日 | 7 分钟阅读

随着现代 JavaScript 的发展,异步功能变得越来越重要,尤其是在 Node.js 应用程序中进行的 I/O 操作的开发中。最初,回调和 Promise 是处理结果非即时操作的唯一方法。虽然这个概念在 ES2017 中引入 async/await 时很有用,但它使得编写异步代码更加可读和可维护。

Async Await 简介

async/await 功能是建立在 Promises 之上的,它保证了一种简洁且看起来像同步的代码语法,用于处理异步操作。它使开发人员能够编写看起来像同步代码的异步代码,编写起来就像理解和维护一样容易。

语法

说明

  • async function:它声明一个异步函数。此函数将始终返回一个 Promise。
  • await:它会暂停 async 函数的执行,直到 Promise 被解析。它允许等待异步操作完成。
  • try...catch:用于错误处理。通过 try...catch 块可以捕获在 async 函数中抛出的任何错误。
  • asyncOperation:它表示一个异步操作,返回一个在一段时间后解析的 Promise(模拟异步任务)。

Code Example

让我们考虑一个使用 async/await 异步从 API 获取数据的场景。

输出

Async Await 的优势

  • 可读性:async/await 语句允许将异步代码组织得更像同步代码,更容易阅读和理解,即使对于初学者也是如此。
  • 错误处理:Async/await 意味着简单的错误处理,可以通过 try...catch 块实现。与基于回调的方法相比,错误管理和调试更容易实现,可以解决错误的发生。
  • 顺序代码:Async/await 使开发人员能够以类似于顺序执行的方式处理异步操作,从而节省了使用 Promise 链式调用或嵌套回调的时间。
  • 调试:此功能消除了处理复杂令牌操作的需要,因为 async/await 代码类似于同步代码,这使得调试过程尽可能简单。开发人员可以插入断点,并且调试器步骤会通过对代码调试过程的无限改进到达。
  • 变量作用域:Async 函数变量是此函数局部的,仅限于此函数,从而避免了命名冲突并提高了代码的封装性。

应避免的陷阱

  • 忘记 await:心不在焉且未能检查是否使用了 await 关键字可能会导致不必要的错误。因此,此函数中的 await 块在等待异步任务完成时会阻止函数的其余部分运行,并帮助函数正确运行,而不是出现错误。
  • 顺序和并行:虽然 async/await 促进了异步任务的同步处理(独立/并行),但它并不一定能处理并行操作。尽管管理许多同时运行的异步任务可能会冒着性能下降的风险,但在与这些类型的任务交互时必须小心,以避免性能瓶颈。
  • 错误传播:解决 async 函数内部发生的错误至关重要。如果这些异常在函数内未被检测到,它们将传播到调用上下文,并在那里可能被未处理的 Promise 拒绝捕获。
  • 阻塞性质:在 async 函数中执行的同步操作的长时间执行会导致事件循环被锁定,从而导致性能下降。此外,强烈建议避免 async 函数阻塞。

其他示例和输出

让我们通过更多示例来理解 async/await 的实际应用

示例 1:文件读取

输出

说明

  • 在这种情况下,我们使用 fs.readFile 获取文件内容,并使用返回 Promise 的 Promise 功能。
  • 通过异步化,我们优先处理 readFile 函数,暂停其执行直到所有 Promise 都得到满足,然后立即记录文件数据。

示例 2:多个 API 请求

输出

说明

  • 检查器打开了展示如何从一个假的 REST API 获取特定用户的用户数据和一些帖子的示例。
  • 使用 await 语句,我们执行一个队列,其中第一个请求先完成,然后才开始第二个请求。

示例 3:并行请求

输出

说明

  • 在前一种情况之后,我们讨论 Promise。每个请求都必须同时发出。
  • 然而,它是针对项目分离且可以并发执行以获得最佳性能的场景的解决方案。

实际应用

Node.js 中 async/await 的几个实际应用如下:

  • Web 服务器:使用 Node.js Web 服务器,async/await 可以应用于处理 HTTP 请求和数据库操作。在这种特定情况下,使用 async/await 进行异步处理比同步处理更有用,例如将用户数据获取到数据库。
  • 微服务:在微服务架构中,async\await 和异步通信是不可或缺的,因为不同的服务是异步工作的。它允许一次处理多个服务调用,并促进服务之间的良好通信。
  • 文件处理:例如,处理 Promise 来执行文件操作的应用程序可以使用 async/await。例如,可以使用 async/await 任务上传器来并行运行一系列文件上传。
  • 并发控制:Async/await 是确保应用程序并发的不错选择。

其他注意事项

  • 错误处理:使用 async/await 时,正确的错误处理至关重要。确保所有潜在错误都已捕获并妥善处理,以防止未处理的 Promise 拒绝。
  • 异步迭代:Async/await 可以与 for...of 循环等功能结合使用,以异步迭代数组、生成器或其他可迭代对象。
  • 中间件:在 Express.js 等框架中,async/await 通常在中间件函数中使用,以在将控制权传递给下一个中间件或路由处理程序之前执行身份验证、日志记录或数据验证等异步任务。
  • 测试:Async/await 通过允许测试在进行断言之前等待 Promise 解析,从而简化了异步代码的测试。Jest 等测试框架内置支持 async/await。
  • 流式处理:虽然 async/await 主要用于处理离散的异步操作,但流式处理大量数据可能仍然受益于传统的基于流的方法,以避免内存开销。

未来考虑

  • ES 模块:JavaScript 编程语言(Node.js)“ESM”的采用可能会带来更多发展,特别是在 Web 浏览器中进行模块加载和执行时。
  • 性能调优:JavaScript 的 async/await 功能使代码开发更加容易。但是,开发人员仍应考虑与此功能相关的性能影响,例如最小化会阻塞执行的操作和优化资源使用。
  • 错误传播:在 async 代码中建立错误传播将是制定可靠的错误处理方法和防止因未处理的异常而崩溃的关键步骤。
  • ESNext 功能:JavaScript 语言的演进带来了新的功能和改进,因此受到开发人员的青睐,并提高了生产力。

结论

总之,async/await 通过提供更自然的语法,在 Node.js 中简化了异步编程的计算。它使编程编写更加容易,因为它有助于使代码既可读又可持续,这与传统的基于回调或基于 Promise 的代码不同。同时,我们必须记住,如果我们想正确使用 async 函数,需要通过 try...catch 块来处理错误,以实现任何级别的健壮性。Node.js 中的 Async/await 标志着异步编程的重大进步,它允许一种更合乎逻辑且易于阅读的处理异步任务的方式。TypeScript 在 Node.js 生态系统中如此普及这一事实,强调了它在现代 JavaScript 开发中的重要性。

总而言之,async/await 是最重要的特性之一,用于在 Node.js 中编写可读的异步代码,从而实现开发人员生产力和代码质量的期望提高。