for await...of

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2020 年 1 月⁩ 起,所有主流浏览器均已支持。

for await...of 语句创建一个循环,迭代异步可迭代对象以及同步可迭代对象。此语句只能在可以使用 await 的上下文中使用,包括在async function 的函数体内部和模块中。

试一试

async function* foo() {
  yield 1;
  yield 2;
}

(async () => {
  for await (const num of foo()) {
    console.log(num);
    // Expected output: 1

    break; // Closes iterator, triggers return
  }
})();

语法

js
for await (variable of iterable)
  statement
variable

在每次迭代中接收序列中的一个值。可以是使用 constletvar 声明的变量,也可以是赋值目标(例如,先前声明的变量、对象属性或解构模式)。使用 var 声明的变量不是循环局部的,即它们与 for await...of 循环处于同一作用域中。

iterable

一个异步可迭代对象或同步可迭代对象。循环操作的值序列的来源。

statement

每次迭代时执行的语句。可以引用 variable。你可以使用块语句来执行多条语句。

描述

for await...of 循环迭代一个可迭代对象时,它首先获取可迭代对象的 [Symbol.asyncIterator]() 方法并调用它,这会返回一个异步迭代器。如果 @asyncIterator 方法不存在,它会查找 [Symbol.iterator]() 方法,该方法会返回一个同步迭代器。然后,同步迭代器会被封装成一个异步迭代器,方法是将 next()return()throw() 方法返回的每个对象封装成一个已解决或已拒绝的 Promise,如果 value 属性本身也是一个 Promise,则该属性也会被解决。然后,循环会重复调用最终异步迭代器的 next() 方法,并 等待 返回的 Promise,从而产生要分配给 variable 的值序列。

当迭代器完成时(等待的 next() 结果是一个带有 done: true 的对象),for await...of 循环退出。与其他循环语句一样,你可以在 statement 中使用控制流语句

  • break 会停止 statement 的执行,并跳转到循环之后的第一条语句。
  • continue 会停止 statement 的执行,并进入循环的下一次迭代。

如果 for await...of 循环提前退出(例如,遇到 break 语句或抛出错误),则会调用迭代器的 return() 方法来执行任何清理操作。在循环退出之前,返回的 Promise 会被等待。

for await...of 的功能通常与 for...of 循环相同,并共享许多相同的语法和语义。但也有一些区别:

  • for await...of 适用于同步和异步可迭代对象,而 for...of 仅适用于同步可迭代对象。
  • for await...of 只能在可以使用 await 的上下文中使用,包括在async function 的函数体内部和模块中。即使可迭代对象是同步的,循环仍然会等待每次迭代的返回值,由于重复的 Promise 解包,导致执行速度变慢。
  • 如果 iterable 是一个产生 Promise 的同步可迭代对象,for await...of 将产生一个已解决值的序列,而 for...of 将产生一个 Promise 序列。(但是,请注意错误处理和清理——参见迭代同步可迭代对象和生成器
  • 对于 for await...ofvariable 可以是标识符 async(例如,for await (async of foo));for...of 禁止这种情况。

for...of 类似,如果使用 using 声明,则变量不能命名为 of

js
for await (using of of []); // SyntaxError

这是为了避免在引入 using 之前,与有效代码 for await (using of []) 之间出现语法歧义。

示例

迭代异步可迭代对象

你也可以迭代一个显式实现了异步可迭代协议的对象。

js
const LIMIT = 3;

const asyncIterable = {
  [Symbol.asyncIterator]() {
    let i = 0;
    return {
      next() {
        const done = i === LIMIT;
        const value = done ? undefined : i++;
        return Promise.resolve({ value, done });
      },
      return() {
        // This will be reached if the consumer called 'break' or 'return' early in the loop.
        return { done: true };
      },
    };
  },
};

(async () => {
  for await (const num of asyncIterable) {
    console.log(num);
  }
})();
// 0
// 1
// 2

迭代异步生成器

由于异步生成器函数的返回值符合异步可迭代协议,它们可以使用 for await...of 进行循环。

js
async function* asyncGenerator() {
  let i = 0;
  while (i < 3) {
    yield i++;
  }
}

(async () => {
  for await (const num of asyncGenerator()) {
    console.log(num);
  }
})();
// 0
// 1
// 2

对于使用 for await...of 迭代异步生成器的更具体示例,请考虑迭代来自 API 的数据。

此示例首先为数据流创建一个异步可迭代对象,然后使用它来查找 API 响应的大小。

js
async function* streamAsyncIterable(stream) {
  const reader = stream.getReader();
  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) return;
      yield value;
    }
  } finally {
    reader.releaseLock();
  }
}

// Fetches data from URL and calculates response size using the async generator.
async function getResponseSize(url) {
  const response = await fetch(url);
  // Will hold the size of the response, in bytes.
  let responseSize = 0;
  // The for-await-of loop. Async iterates over each portion of the response.
  for await (const chunk of streamAsyncIterable(response.body)) {
    // Incrementing the total response length.
    responseSize += chunk.length;
  }

  console.log(`Response Size: ${responseSize} bytes`); // "Response Size: 1071472"
  return responseSize;
}
getResponseSize("https://jsonplaceholder.typicode.com/photos");

迭代同步可迭代对象和生成器

for await...of 循环也消费同步可迭代对象和生成器。在这种情况下,它在内部等待发出的值,然后将其分配给循环控制变量。

js
function* generator() {
  yield 0;
  yield 1;
  yield Promise.resolve(2);
  yield Promise.resolve(3);
  yield 4;
}

(async () => {
  for await (const num of generator()) {
    console.log(num);
  }
})();
// 0
// 1
// 2
// 3
// 4

// compare with for-of loop:

for (const numOrPromise of generator()) {
  console.log(numOrPromise);
}
// 0
// 1
// Promise { 2 }
// Promise { 3 }
// 4

注意:请注意从同步生成器中产生被拒绝的 Promise。在这种情况下,for await...of 在消费被拒绝的 Promise 时会抛出错误,并且不会调用该生成器中的 finally 块。如果你需要使用 try/finally 释放一些已分配的资源,这可能是不理想的。

js
function* generatorWithRejectedPromises() {
  try {
    yield 0;
    yield 1;
    yield Promise.resolve(2);
    yield Promise.reject(new Error("failed"));
    yield 4;
    throw new Error("throws");
  } finally {
    console.log("called finally");
  }
}

(async () => {
  try {
    for await (const num of generatorWithRejectedPromises()) {
      console.log(num);
    }
  } catch (e) {
    console.log("caught", e);
  }
})();
// 0
// 1
// 2
// caught Error: failed

// compare with for-of loop:

try {
  for (const numOrPromise of generatorWithRejectedPromises()) {
    console.log(numOrPromise);
  }
} catch (e) {
  console.log("caught", e);
}
// 0
// 1
// Promise { 2 }
// Promise { <rejected> Error: failed }
// 4
// caught Error: throws
// called finally

为了使同步生成器的 finally 块始终被调用,请使用适当形式的循环——对于异步生成器使用 for await...of,对于同步生成器使用 for...of——并在循环内部明确等待产生的 Promise。

js
(async () => {
  try {
    for (const numOrPromise of generatorWithRejectedPromises()) {
      console.log(await numOrPromise);
    }
  } catch (e) {
    console.log("caught", e);
  }
})();
// 0
// 1
// 2
// caught Error: failed
// called finally

规范

规范
ECMAScript® 2026 语言规范
# sec-for-in-and-for-of-statements

浏览器兼容性

另见