for await...of
for await...of
语句创建一个迭代 异步可迭代对象 和 同步迭代 的循环。该语句只能在可以使用 await
的上下文中使用,其中包括 异步函数 主体内部和 module 内部。
¥The for await...of
statement creates a loop iterating over async iterable objects as well as sync iterables. This statement can only be used in contexts where await
can be used, which includes inside an async function body and in a module.
Try it
语法
描述
¥Description
当 for await...of
循环迭代可迭代对象时,它首先获取可迭代对象的 [Symbol.asyncIterator]()
方法并调用它,该方法返回 异步迭代器。如果 @asyncIterator
方法不存在,则会查找 [Symbol.iterator]()
方法,该方法返回 同步迭代器。然后,通过将从 next()
、return()
和 throw()
方法返回的每个对象封装到已解决或已拒绝的 Promise 中,将返回的同步迭代器封装到异步迭代器中,如果 value
属性也是一个 Promise,则将其解析。然后,循环重复调用最终异步迭代器的 next()
方法和 awaits 返回的 Promise,生成要分配给 variable
的值序列。
¥When a for await...of
loop iterates over an iterable, it first gets the iterable's [Symbol.asyncIterator]()
method and calls it, which returns an async iterator. If the @asyncIterator
method does not exist, it then looks for an [Symbol.iterator]()
method, which returns a sync iterator. The sync iterator returned is then wrapped into an async iterator by wrapping every object returned from the next()
, return()
, and throw()
methods into a resolved or rejected promise, with the value
property resolved if it's also a promise. The loop then repeatedly calls the final async iterator's next()
method and awaits the returned promise, producing the sequence of values to be assigned to variable
.
当迭代器完成时,for await...of
循环退出(等待的 next()
结果是具有 done: true
的对象)。与其他循环语句一样,你可以在 statement
内使用 控制流语句:
¥A for await...of
loop exits when the iterator has completed (the awaited next()
result is an object with done: true
). Like other looping statements, you can use control flow statements inside statement
:
如果 for await...of
循环提前退出(例如遇到 break
语句或抛出错误),则调用迭代器的 return()
方法来执行任何清理。在循环退出之前等待返回的 Promise。
¥If the for await...of
loop exited early (e.g. a break
statement is encountered or an error is thrown), the return()
method of the iterator is called to perform any cleanup. The returned promise is awaited before the loop exits.
for await...of
的功能通常与 for...of
循环相同,并且共享许多相同的语法和语义。有一些区别:
¥for await...of
generally functions the same as the for...of
loop and shares many of the same syntax and semantics. There are a few differences:
for await...of
适用于同步和异步迭代,而for...of
仅适用于同步迭代。for await...of
只能在可以使用await
的上下文中使用,其中包括 异步函数 主体内部和 module 内部。即使可迭代对象是同步的,循环仍然会等待每次迭代的返回值,从而由于重复的承诺展开而导致执行速度变慢。- 如果
iterable
是一个产生 Promise 的同步迭代,那么for await...of
将产生一系列解析值,而for...of
将产生一系列 Promise。(但是,请注意错误处理和清理 - 请参阅 迭代同步迭代器和生成器) - 对于
for await...of
,variable
可以是标识符async
(例如for await (async of foo)
);for...of
禁止这种情况。
示例
迭代异步可迭代对象
¥Iterating over async iterables
你还可以迭代显式实现异步可迭代协议的对象:
¥You can also iterate over an object that explicitly implements async iterable protocol:
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
迭代异步生成器
¥Iterating over async generators
由于异步生成器函数的返回值符合异步可迭代协议,因此可以使用 for await...of
来循环它们。
¥Since the return values of async generator functions conform to the async iterable protocol,
they can be looped using for await...of
.
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 的数据。
¥For a more concrete example of iterating over an async generator using for await...of
, consider iterating over data from an API.
此示例首先为数据流创建一个异步迭代,然后使用它来查找 API 响应的大小。
¥This example first creates an async iterable for a stream of data, then uses it to find the size of the response from the API.
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");
迭代同步迭代器和生成器
¥Iterating over sync iterables and generators
for await...of
循环还消耗同步迭代器和生成器。在这种情况下,它会在将发出的值分配给循环控制变量之前在内部等待它们。
¥for await...of
loop also consumes sync iterables and generators. In that case it internally awaits emitted values before assign them to the loop control variable.
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
注意:请注意从同步生成器中产生被拒绝的承诺。在这种情况下,
for await...of
在使用被拒绝的 Promise 时会抛出异常,并且不会在该生成器中调用finally
块。如果你需要使用try/finally
释放一些分配的资源,这可能是不可取的。¥Note: Be aware of yielding rejected promises from a sync generator. In such case,
for await...of
throws when consuming the rejected promise and DOESN'T CALLfinally
blocks within that generator. This can be undesirable if you need to free some allocated resources withtry/finally
.
function* generatorWithRejectedPromises() {
try {
yield 0;
yield 1;
yield Promise.resolve(2);
yield Promise.reject(3);
yield 4;
throw 5;
} 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 3
// 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> 3 }
// 4
// caught 5
// called finally
要使同步生成器的 finally
块始终被调用,请使用适当的循环形式 — for await...of
用于异步生成器,for...of
用于同步生成器 — 并在循环内显式等待产生的 Promise。
¥To make finally
blocks of a sync generator always called, use the appropriate form of the loop — for await...of
for the async generator and for...of
for the sync one — and await yielded promises explicitly inside the loop.
(async () => {
try {
for (const numOrPromise of generatorWithRejectedPromises()) {
console.log(await numOrPromise);
}
} catch (e) {
console.log("caught", e);
}
})();
// 0
// 1
// 2
// caught 3
// called finally
规范
Specification |
---|
ECMAScript Language Specification # sec-for-in-and-for-of-statements |
浏览器兼容性
BCD tables only load in the browser
也可以看看
¥See also