JavaScript Async/Await 比普通 Promises 更好的 7 个理由(教程)

2025-05-24

JavaScript Async/Await 比普通 Promises 更好的 7 个理由(教程)

Async/await 是在 NodeJS 7.6 中引入的,目前所有现代浏览器都支持它。我认为它是 2017 年以来 JS 最伟大的新增功能。如果你还不信,这里有一些理由和示例,告诉你为什么你应该立即采用它,永不后悔。

Async/Await 101

对于那些以前从未听说过这个话题的人,这里有一个简短的介绍

  • Async/await 是一种编写异步代码的新方法。以前的异步代码替代方案是回调和 Promise。
  • Async/await 实际上只是基于 Promise 的语法糖。它不能与普通回调或 Node 回调一起使用。
  • Async/await 与承诺一样,是非阻塞的。
  • Async/await 使异步代码的外观和行为更接近同步代码。这正是它的强大之处。

句法

假设一个函数getJSON返回一个 Promise,并且该 Promise 解析后返回一个 JSON 对象。我们只想调用它并记录该 JSON,然后返回"done"

这是使用承诺实现它的方法

const makeRequest = () =>
getJSON()
.then(data => {
console.log(data)
return "done"
})
makeRequest()

使用 async/await 后的效果如下

这里有一些不同之处

  1. async我们的函数前面有 关键字。该await关键字只能在用 定义的函数中使用async。任何async函数都会隐式返回一个 Promise,而 Promise 的 resolve 值将是你return从函数中获取的任何值(在我们的例子中是字符串"done")。

  2. 上述观点意味着我们不能await在代码的顶层使用它,因为它不在async函数内部。

  3. await getJSON()意味着console.log调用将等待直到getJSON()承诺解决并打印其值。

为什么它更好?

  1. 简洁明了
    看看我们少写了多少代码!即使在上面那个设计精巧的例子中,我们也明显节省了相当多的代码。我们不必编写.then,不必创建匿名函数来处理响应,也data不必为不需要的变量命名。我们还避免了代码嵌套。这些小优势很快就会累积起来,在接下来的代码示例中会更加明显。

  2. 错误处理
    Async/await 终于可以用同一种结构(也就是经典的 )来处理同步和异步错误try/catch。在下面使用 Promise 的示例中,try/catchif 不会处理JSON.parse失败,因为它发生在 Promise 内部。我们需要调用.catchPromise 并复制错误处理代码,这些代码(希望)会比console.log你的生产环境代码更复杂。

    现在看看使用 async/await 的相同代码。该catch块现在将处理解析错误。

  3. 条件
    想象一下下面的代码,它获取一些数据并决定是否应该返回该数据或根据数据中的某些值获取更多详细信息。

    光是看着这些代码就让人头疼。很容易迷失在那些嵌套(6 层)、括号和 return 语句中,而这些语句只需要将最终结果传递到主 Promise 即可。

    使用 async/await 重写后,此示例变得更具可读性。

  4. 中间值
    你可能遇到过这种情况:你调用 a promise1,然后使用它的返回值来调用promise2,最后使用两个 Promise 的结果来调用 a promise3。你的代码很可能看起来像这样

    如果promise3没有 require,value1那么可以很容易地将 Promise 嵌套扁平化一些。如果你无法忍受这种情况,你可以将值 1 和 2 都包装在 a 中Promise.all,以避免更深的嵌套,就像这样

    这种方法为了可读性牺牲了语义。除了避免嵌套 Promise 之外, value1&没有必要value2放在数组里。
    同样的逻辑在 async/await 中变得异常简单直观。它会让你不禁思考,你之前为了让 Promise 看起来不那么丑陋而浪费的时间,其实可以做很多事情。

  5. 错误堆栈
    想象一段代码,它在一个链中调用多个承诺,并且在链中的某个地方抛出了一个错误。

    Promise 链返回的错误堆栈无法提供任何错误发生位置的线索。更糟糕的是,它具有误导性;它包含的唯一函数名是 ,callAPromise而这个错误与它无关(不过文件和行号仍然有用)。
    然而,async/await 的错误堆栈指向了包含错误的函数。

    当你在本地环境中开发并在编辑器中打开文件时,这算不上什么大优势,但当你试图理解来自生产服务器的错误日志时,它就非常有用了。在这种情况下,知道错误发生在哪里makeRequest比知道错误来自一个thenthen一个then……要好得多。

  6. 调试
    使用 async/await 的一大优势在于它更容易调试。调试 Promise 一直以来都很痛苦,原因有两个

    1. 您不能在返回表达式(无主体)的箭头函数中设置断点。

      带有链式承诺的代码片段尝试在此处的任意位置设置断点

    2. 如果在块内设置断点.then并使用诸如步进之类的调试快捷方式,则调试器将不会移动到以下内容,.then因为它仅“步进”同步代码。

      使用 async/await 时,您不再需要那么多箭头函数,而且您可以像执行正常的同步调用一样逐步执行 await 调用。

      连续等待的代码片段

  7. await最后
    但同样重要的是,await可以用于同步和异步表达式。例如,你可以写await 5,它相当于Promise.resolve(5)。乍一看,这似乎没什么用,但在编写库或实用程序函数时,如果你不知道输入是同步的还是异步的,这实际上是一个很大的优势。

    假设你想记录应用程序中执行某些 API 调用所花费的时间,并决定为此创建一个通用函数。以下是使用 Promise 实现的示例:

    你知道所有 API 调用都会返回 Promise,但如果你用同一个函数来记录同步函数的耗时,会发生什么?它会抛出一个错误,因为同步函数没有返回 Promise。避免这种情况的常用方法是包装makeRequest()Promise.resolve()

    如果您使用 async/await,则不必担心这些情况,因为 await 允许您安全地处理任何值,无论是否是承诺。

综上所述

Async/await 是 JavaScript 近几年新增的最具革命性的功能之一。它让你意识到 Promise 的语法混乱,并提供了一个直观的替代方案。

担忧

您可能对使用 async/await 存在一些合理的怀疑,因为它会使异步代码不那么明显:我们的眼睛学会了在看到回调或时识别异步代码.then,您的眼睛需要几周的时间来适应新的标志,但 C# 已经拥有此功能多年,熟悉它的人都知道,这种轻微的、暂时的不便值得承受。

在推特上关注我@imgaafar

本文最初发表于 Hackernoon

文章来源:https://dev.to/gafi/7-reasons-to-always-use-async-await-over-plain-promises-tutorial-4ej9
PREV
我建了一个网站,里面有最好的编码字体
NEXT
你需要了解的 7 个令人兴奋的 JavaScript 新功能