异步函数

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since April 2017.

async function 声明创建一个给定名称的新异步函数的 binding。函数体内允许使用 await 关键字,从而能够以更简洁的风格编写基于 Promise 的异步行为,并避免需要显式配置 Promise 链。

¥The async function declaration creates a binding of a new async function to a given name. The await keyword is permitted within the function body, enabling asynchronous, promise-based behavior to be written in a cleaner style and avoiding the need to explicitly configure promise chains.

你还可以使用 async function 表达.a 定义异步函数。

¥You can also define async functions using the async function expression.

Try it

语法

¥Syntax

js
async function name(param0) {
  statements
}
async function name(param0, param1) {
  statements
}
async function name(param0, param1, /* …, */ paramN) {
  statements
}

注意:asyncfunction 之间不能有行终止符,否则一个分号就是 自动插入,导致 async 成为标识符,其余成为 function 声明。

¥Note: There cannot be a line terminator between async and function, otherwise a semicolon is automatically inserted, causing async to become an identifier and the rest to become a function declaration.

参数

¥Parameters

name

函数的名称。

param Optional

函数的形式参数的名称。有关参数的语法,请参阅 函数参考

statements Optional

包含函数体的语句。可以使用 await 机制。

描述

¥Description

async function 声明创建 AsyncFunction 对象。每次调用异步函数时,它都会返回一个新的 Promise,该 Promise 将使用异步函数返回的值进行解析,或者因异步函数内未捕获的异常而被拒绝。

¥An async function declaration creates an AsyncFunction object. Each time when an async function is called, it returns a new Promise which will be resolved with the value returned by the async function, or rejected with an exception uncaught within the async function.

异步函数可以包含零个或多个 await 表达式。Await 表达式通过暂停执行直到返回的 Promise 得到满足或拒绝,使返回 Promise 的函数表现得好像它们是同步的。Promise 的解析值被视为等待表达式的返回值。使用 asyncawait 可以在异步代码周围使用普通 try / catch 块。

¥Async functions can contain zero or more await expressions. Await expressions make promise-returning functions behave as though they're synchronous by suspending execution until the returned promise is fulfilled or rejected. The resolved value of the promise is treated as the return value of the await expression. Use of async and await enables the use of ordinary try / catch blocks around asynchronous code.

注意:await 关键字仅在常规 JavaScript 代码的异步函数内有效。如果你在异步函数体之外使用它,你将得到 SyntaxError

¥Note: The await keyword is only valid inside async functions within regular JavaScript code. If you use it outside of an async function's body, you will get a SyntaxError.

await 可与 JavaScript 模块。 单独使用

¥await can be used on its own with JavaScript modules.

注意:async/await 的目的是简化使用基于 Promise 的 API 所需的语法。async/await 的行为类似于结合 generators 和 Promise。

¥Note: The purpose of async/await is to simplify the syntax necessary to consume promise-based APIs. The behavior of async/await is similar to combining generators and promises.

异步函数总是返回一个承诺。如果异步函数的返回值不是显式的 Promise,它将隐式封装在 Promise 中。

¥Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.

例如,考虑以下代码:

¥For example, consider the following code:

js
async function foo() {
  return 1;
}

它类似于:

¥It is similar to:

js
function foo() {
  return Promise.resolve(1);
}

注意:

¥Note:

尽管异步函数的返回值的行为就像封装在 Promise.resolve 中一样,但它们并不等效。

¥Even though the return value of an async function behaves as if it's wrapped in a Promise.resolve, they are not equivalent.

异步函数将返回不同的引用,而如果给定值是承诺,则 Promise.resolve 返回相同的引用。

¥An async function will return a different reference, whereas Promise.resolve returns the same reference if the given value is a promise.

当你想要检查异步函数的承诺和返回值的相等性时,这可能是一个问题。

¥It can be a problem when you want to check the equality of a promise and a return value of an async function.

js
const p = new Promise((res, rej) => {
  res(1);
});

async function asyncReturn() {
  return p;
}

function basicReturn() {
  return Promise.resolve(p);
}

console.log(p === basicReturn()); // true
console.log(p === asyncReturn()); // false

异步函数的主体可以被认为是被零个或多个等待表达式分割。顶层代码,直到并包括第一个等待表达式(如果有),都是同步运行的。这样,没有 await 表达式的异步函数将同步运行。但是,如果函数体内有 await 表达式,则异步函数将始终异步完成。

¥The body of an async function can be thought of as being split by zero or more await expressions. Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.

例如:

¥For example:

js
async function foo() {
  await 1;
}

它也相当于:

¥It is also equivalent to:

js
function foo() {
  return Promise.resolve(1).then(() => undefined);
}

每个 await 表达式之后的代码可以被认为存在于 .then 回调中。通过这种方式,通过函数的每个可重入步骤逐步构建承诺链。返回值构成了链中的最后一个环节。

¥Code after each await expression can be thought of as existing in a .then callback. In this way a promise chain is progressively constructed with each reentrant step through the function. The return value forms the final link in the chain.

在下面的示例中,我们连续等待两个 Promise。功能 foo 的进展分三个阶段进行。

¥In the following example, we successively await two promises. Progress moves through function foo in three stages.

  1. 函数 foo 主体的第一行同步执行,并使用配置有待处理承诺的等待表达式。然后,foo 的进程将暂停,并将控制权交还给调用 foo 的函数。
  2. 一段时间后,当第一个承诺已履行或拒绝时,控制权移回 foo。第一个承诺履行的结果(如果没有被拒绝)从等待表达式返回。这里 1 被分配给 result1。继续进行,并计算第二个等待表达式。再次,foo 的进度被暂停,控制权被让出。
  3. 一段时间后,当第二个承诺已履行或拒绝时,控制权重新进入 foo。第二个 promise 解析的结果从第二个 await 表达式返回。这里 2 被分配给 result2。控制移至返回表达式(如果有)。默认返回值 undefined 作为当前 Promise 的解析值返回。
js
async function foo() {
  const result1 = await new Promise((resolve) =>
    setTimeout(() => resolve("1")),
  );
  const result2 = await new Promise((resolve) =>
    setTimeout(() => resolve("2")),
  );
}
foo();

请注意,承诺链并不是一次性建立起来的。相反,承诺链是分阶段构建的,控制权依次从异步函数中产生并返回给异步函数。因此,在处理并发异步操作时,我们必须注意错误处理行为。

¥Note how the promise chain is not built-up in one go. Instead, the promise chain is constructed in stages as control is successively yielded from and returned to the async function. As a result, we must be mindful of error handling behavior when dealing with concurrent asynchronous operations.

例如,在以下代码中,即使已沿着 Promise 链进一步配置了 .catch 处理程序,也会引发未处理的 Promise 拒绝错误。这是因为在控制权从 p1 返回之前,p2 不会成为 "连接到" 的承诺链。

¥For example, in the following code an unhandled promise rejection error will be thrown, even if a .catch handler has been configured further along the promise chain. This is because p2 will not be "wired into" the promise chain until control returns from p1.

js
async function foo() {
  const p1 = new Promise((resolve) => setTimeout(() => resolve("1"), 1000));
  const p2 = new Promise((_, reject) => setTimeout(() => reject("2"), 500));
  const results = [await p1, await p2]; // Do not do this! Use Promise.all or Promise.allSettled instead.
}
foo().catch(() => {}); // Attempt to swallow all errors...

async function 声明的行为与 function 声明类似 - 它们是 hoisted 到其范围的顶部,可以在其范围内的任何位置调用,并且只能在某些上下文中重新声明。

¥async function declarations behave similar to function declarations — they are hoisted to the top of their scope and can be called anywhere in their scope, and they can be redeclared only in certain contexts.

示例

¥Examples

异步函数和执行顺序

¥Async functions and execution order

js
function resolveAfter2Seconds() {
  console.log("starting slow promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("slow");
      console.log("slow promise is done");
    }, 2000);
  });
}

function resolveAfter1Second() {
  console.log("starting fast promise");
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("fast");
      console.log("fast promise is done");
    }, 1000);
  });
}

async function sequentialStart() {
  console.log("== sequentialStart starts ==");

  // 1. Start a timer, log after it's done
  const slow = resolveAfter2Seconds();
  console.log(await slow);

  // 2. Start the next timer after waiting for the previous one
  const fast = resolveAfter1Second();
  console.log(await fast);

  console.log("== sequentialStart done ==");
}

async function sequentialWait() {
  console.log("== sequentialWait starts ==");

  // 1. Start two timers without waiting for each other
  const slow = resolveAfter2Seconds();
  const fast = resolveAfter1Second();

  // 2. Wait for the slow timer to complete, and then log the result
  console.log(await slow);
  // 3. Wait for the fast timer to complete, and then log the result
  console.log(await fast);

  console.log("== sequentialWait done ==");
}

async function concurrent1() {
  console.log("== concurrent1 starts ==");

  // 1. Start two timers concurrently and wait for both to complete
  const results = await Promise.all([
    resolveAfter2Seconds(),
    resolveAfter1Second(),
  ]);
  // 2. Log the results together
  console.log(results[0]);
  console.log(results[1]);

  console.log("== concurrent1 done ==");
}

async function concurrent2() {
  console.log("== concurrent2 starts ==");

  // 1. Start two timers concurrently, log immediately after each one is done
  await Promise.all([
    (async () => console.log(await resolveAfter2Seconds()))(),
    (async () => console.log(await resolveAfter1Second()))(),
  ]);
  console.log("== concurrent2 done ==");
}

sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast"

// wait above to finish
setTimeout(sequentialWait, 4000); // after 2 seconds, logs "slow" and then "fast"

// wait again
setTimeout(concurrent1, 7000); // same as sequentialWait

// wait again
setTimeout(concurrent2, 10000); // after 1 second, logs "fast", then after 1 more second, "slow"

等待和并发

¥await and concurrency

sequentialStart 中,第一个 await 的执行暂停 2 秒,然后第二个 await 的执行暂停 2 秒。在第一个计时器触发后才会创建第二个计时器,因此代码会在 3 秒后完成。

¥In sequentialStart, execution suspends 2 seconds for the first await, and then another second for the second await. The second timer is not created until the first has already fired, so the code finishes after 3 seconds.

sequentialWait 中,两个定时器均被创建,然后是 await。计时器同时运行,这意味着代码在 2 秒而不是 3 秒内完成,即最慢的计时器。然而,await 调用仍然串行运行,这意味着第二个 await 将等待第一个调用完成。在这种情况下,最快的计时器的结果在最慢的计时器之后处理。

¥In sequentialWait, both timers are created and then awaited. The timers run concurrently, which means the code finishes in 2 rather than 3 seconds, i.e. the slowest timer. However, the await calls still run in series, which means the second await will wait for the first one to finish. In this case, the result of the fastest timer is processed after the slowest.

如果你希望在两个或多个作业同时运行并完成后安全地执行其他作业,则必须在该作业之前等待对 Promise.all()Promise.allSettled() 的调用。

¥If you wish to safely perform other jobs after two or more jobs run concurrently and are complete, you must await a call to Promise.all() or Promise.allSettled() before that job.

警告:功能 sequentialWaitconcurrent1 在功能上并不等同。

¥Warning: The functions sequentialWait and concurrent1 are not functionally equivalent.

sequentialWait 中,如果 Promise fast 在 Promise slow 完成之前拒绝,那么无论调用者是否配置了 catch 子句,都会引发未处理的 Promise 拒绝错误。

¥In sequentialWait, if promise fast rejects before promise slow is fulfilled, then an unhandled promise rejection error will be raised, regardless of whether the caller has configured a catch clause.

concurrent1 中,Promise.all 一次性连接了 promise 链,这意味着无论拒绝 promise 的顺序如何,操作都会快速失败,并且错误将始终发生在配置的 promise 链中,使其能够被捕获在 正常方式。

¥In concurrent1, Promise.all wires up the promise chain in one go, meaning that the operation will fail-fast regardless of the order of rejection of the promises, and the error will always occur within the configured promise chain, enabling it to be caught in the normal way.

使用异步函数重写 Promise 链

¥Rewriting a Promise chain with an async function

返回 Promise 的 API 将产生一个 Promise 链,并将函数分成许多部分。考虑以下代码:

¥An API that returns a Promise will result in a promise chain, and it splits the function into many parts. Consider the following code:

js
function getProcessedData(url) {
  return downloadData(url) // returns a promise
    .catch((e) => downloadFallbackData(url)) // returns a promise
    .then((v) => processDataInWorker(v)); // returns a promise
}

可以使用单个异步函数重写它,如下所示:

¥it can be rewritten with a single async function as follows:

js
async function getProcessedData(url) {
  let v;
  try {
    v = await downloadData(url);
  } catch (e) {
    v = await downloadFallbackData(url);
  }
  return processDataInWorker(v);
}

或者,你可以将 Promise 链接到 catch()

¥Alternatively, you can chain the promise with catch():

js
async function getProcessedData(url) {
  const v = await downloadData(url).catch((e) => downloadFallbackData(url));
  return processDataInWorker(v);
}

在两个重写版本中,请注意 return 关键字后面没有 await 语句,尽管这也是有效的:异步函数的返回值隐式封装在 Promise.resolve 中 - 如果它本身还不是一个承诺(如示例中所示)。

¥In the two rewritten versions, notice there is no await statement after the return keyword, although that would be valid too: The return value of an async function is implicitly wrapped in Promise.resolve - if it's not already a promise itself (as in the examples).

规范

Specification
ECMAScript Language Specification
# sec-async-function-definitions

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看