发布于 2026-01-05 0 阅读
0

关于 JavaScript 中的 async/await,你需要了解的 6 个要点:ECMAScript 提案:顶层 await

关于 JavaScript 中的 async/await,你需要了解的 6 个要点

ECMAScript提案:顶级await

ECMAScript提案:顶级await

如果你遇到过类似下面的代码,那么这篇文章将从多方面帮助你😁。

fetchPizzas()
  .then((pizzas) = {
    return sortByToppings(pizzas)
      .then((pizzas) = {
        return checkDeliveryOptions(pizzas)
          .then((pizzasWithDelivery) = {
            return checkBirthdayGift(pizzasWithDelivery)
              .then((pizza) = {
                return sendToCustomer(pizza);
              });
          });
      });
  });
Enter fullscreen mode Exit fullscreen mode

一点背景介绍

很多时候,我们需要按顺序执行一系列任务。例如,文件处理、根据前一次调用结果多次调用数据库,或者按顺序调用多个 API,其中一次调用依赖于另一次调用。

在引入 Promise 之前async/await,许多人使用回调函数setTimeOut来模拟他们想要的行为(也就是所谓的回调地狱)。后来,人们开始使用 Promise,这使得代码更易读,但当调用次数很高时,最终还是会陷入同样的​​困境(也就是所谓的 Promise 地狱)。

异步函数

在 JavaScript 中,函数是async指通过事件循环异步执行,并使用隐式 Promise 返回结果的程序。此外,其结果的类型必须是AsyncFunction对象。

这个函数其实就是Promise生成器的组合。我不会详细介绍生成器,但它们通常包含一个或多个yield关键字。

现在让我们看看这个async函数是如何工作的。假设我们有一个返回字符串的函数:

function hi() {
  return 'Hi from JavaScript';
}

hi(); // 'Hi from JavaScript'
Enter fullscreen mode Exit fullscreen mode

如果在函数前面加上 ` async\promise`,它就不再返回字符串,而是返回一个 Promise,该 Promise 会自动包装字符串值。

async function hi() {
  return 'Hi from JavaScript';
}

hi(); // Promise {<resolved>: "Hi from JavaScript"}
Enter fullscreen mode Exit fullscreen mode

现在,为了从承诺中获得价值,我们像以前一样行事:

hi().then(console.log); // 'Hi from JavaScript'
Enter fullscreen mode Exit fullscreen mode

你可能想知道这如何能帮助解决 Promise 地狱问题。请耐心听我解释,我们会一步一步地用例子来讲解,这样讲完之后你就会明白了。

等待

await关键字使 JavaScript 引擎等待 Promise 被解析/拒绝,并返回其结果。此关键字只能在async函数内部使用。

const doSomething = async () => {
  console.log(await hi())
};

// 'Hi from JavaScript'
Enter fullscreen mode Exit fullscreen mode

你可能会认为,由于await`resolve` 会强制 JavaScript 引擎等待,因此会消耗一些 CPU 资源。但事实并非如此,因为引擎可以在等待 Promise 被解析/拒绝期间执行其他脚本。此外,这比使用 `resolve` 和 `reject` 要优雅promises得多.then

警告:如果在普通函数内部尝试调用async函数await,将会收到语法错误。

function doSomething() {
  await hi(); // Uncaught SyntaxError: await is only valid in async function
}
Enter fullscreen mode Exit fullscreen mode

一个小陷阱

大多数刚开始使用编程语言的人async/await都会忘记,他们不能async在顶层代码中调用函数。这是因为await普通函数内部不能有 `<script>` 标签,而顶层函数默认就是普通函数。

let response = await hi(); // syntax error in top-level code
console.log(response);
Enter fullscreen mode Exit fullscreen mode

不过,你可以将代码包装在一个async IIFE(立即执行的函数)中,然后直接在函数中调用它:

(async () => {
  let response = await hi(); 
  console.log(response); // 'Hi from JavaScript'
  ...
})();
Enter fullscreen mode Exit fullscreen mode

更新:正如Nick Tyler在评论中提到的,目前有一个第三阶段的提案,旨在支持await顶层代码。敬请关注后续进展:

GitHub 标志 tc39 / proposal-top-level-await

ECMAScript 的顶级 `await` 提案(第三阶段)

ECMAScript提案:顶级await

冠军:迈尔斯·博林斯

状态:第三阶段

概要

顶层模块await使模块能够像大型异步函数一样运行:有了顶层模块await,ECMAScript 模块 (ESM) 可以await调用资源,导致其他调用它们的模块import在开始执行其主体之前必须等待。

动机

IIAFE 的局限性

由于该功能await仅在函数内部可用async,因此模块可以await通过将该代码分解为一个函数,在启动时执行的代码中包含该async功能:

// awaiting.mjs 
import  {  process  }  from  "./some-module.mjs" ; 
let  output ; 
async  function  main ( )  { 
  const  dynamic  =  await  import ( computedModuleSpecifier ) ; 
  const  data  =  await  fetch ( url ) ; 
  output  =  process ( dynamic . default ,  data ) ; 
} 
main ( ) ; 
export  {  output  } ;
Enter fullscreen mode Exit fullscreen mode

这种模式也可以立即调用。你可以称之为立即调用的异步函数……

错误处理

正如我之前所说,大多数async函数都可以用 Promise 编写成普通函数。然而,函数在错误处理方面async有所欠缺。如果一个 await 调用失败,异常会被自动捕获,并且对象会通过隐式返回的 Promise 传递给调用者。error-proneError

在此之前,我们必须拒绝从普通函数返回的 Promise,并.catch在调用者中使用 `is`。我见过很多地方,开发人员使用 `try/catch` 语句并抛出一个新的异常,这意味着堆栈跟踪会被重置。

async function hi() {
  throw new Error("Whoops!");
};

async function doSomething() {

  try {
    let response = await hi();
    return response;
  } catch(err) {    
    console.log(err);
  }
}

doSomething();
Enter fullscreen mode Exit fullscreen mode

或者,您可以避免这种情况,try/catch因为调用生成的 Promisehi会被拒绝。然后只需使用 ` .catchgetError` 方法来处理错误即可。

async function hi() {
  throw new Error("Whoops!");
};

async function doSomething() {
  let response = await hi();
  return response;
}

doSomething().catch(err => {
  console.log(err);
});
Enter fullscreen mode Exit fullscreen mode

如果你认为更适合你的情况,可以完全忽略 catch 语句,直接使用全局异常处理程序来处理所有异常。例如,像这样使用mixinonrejectionhandled属性的代码。WindowsEventHandlers

window.onrejectionhandled = function(e) {
  console.log(e.reason);
}
Enter fullscreen mode Exit fullscreen mode

承诺所有兼容性

你可以使用async/await`aside`Promise.all来等待多个 Promise 完成:

const responses = await Promise.all([
  fetch('yashints.dev/rss'),
  hi(),
  ...
])
Enter fullscreen mode Exit fullscreen mode

如果发生错误,它会像往常一样从失败的 Promise 传播,Promise.all然后变成一个异常,您可以使用上述任何方法捕获该异常。

await可以接受“thenable”

类似地promise.then,如果你有一个具有.then方法的对象,await它将被接受。这是为了支持这样一种场景:第三方对象不是 Promise,但与 Promise 兼容(它支持.then),在这种情况下,只需将其与一起使用即可await

class Greeting {
  constructor(name) {
    this.name = name;
  }

  then(resolve, reject) {
    console.log(resolve);

    setTimeout(() => resolve(`Hi ${this.name}`));
  }
};

async function greet() {
  const greeting = await Greeting('Yaser');

  console.log(greeting); // Hi Yaser
};

greet();
Enter fullscreen mode Exit fullscreen mode

async类方法

你可以拥有一个async类方法。只需在方法名前加上async`.` 即可。

class Order {
  async deliver() {
    return await Promise.resolve('Pizza');
  }
}

new Order()
  .deliver()
  .then(console.log); // Pizza
Enter fullscreen mode Exit fullscreen mode

概括

简单回顾一下我们目前讨论的内容:

  1. async关键字使方法异步化,从而总是返回一个 Promise,并允许await使用。
  2. await在 Promise 前加上关键字会让 JavaScript 等待该 Promise 被解析/拒绝。如果 Promise 被拒绝,则会抛出异常;否则,会返回结果。
  3. 它们共同为我们提供了编写简洁、更易于测试的异步代码的绝佳机会。
  4. 有了async/await它们你就不需要了.then/.catch,但请注意,它们仍然是基于承诺的。
  5. 你可以使用它Promise.all来等待多个async函数调用。
  6. async类中可以包含方法。

我知道网上有很多很棒的文章async/await,但我还是想涵盖一些我需要经常提醒自己的要点。希望这篇文章能帮助你找到编写简洁异步 JavaScript 所需的大部分内容。

尽情探索这些景点吧。

文章来源:https://dev.to/yashints/6-points-you-need-to-know-about-async-await-in-javascript-1d1o