for await...of
试一试
语法
描述
当 for await...of
循环迭代可迭代对象时,它首先获取可迭代对象的 [Symbol.asyncIterator]()
方法并调用它,该方法返回一个 异步迭代器。如果 @asyncIterator
方法不存在,它将寻找 [Symbol.iterator]()
方法,该方法返回一个 同步迭代器。然后通过将从 next()
、return()
和 throw()
方法返回的每个对象包装到已解析或被拒绝的 Promise 中来将返回的同步迭代器包装成异步迭代器,如果 value
属性也是 Promise,则解析该属性。然后,循环重复调用最终异步迭代器的 next()
方法并 等待 返回的 Promise,从而生成要分配给 变量
的值序列。
for await...of
循环在迭代器完成时退出(等待的 next()
结果是具有 done: true
的对象)。与其他循环语句一样,可以在 语句
中使用 控制流语句
如果 for await...of
循环提前退出(例如,遇到 break
语句或抛出错误),则调用迭代器的 return()
方法执行任何清理操作。等待返回的 Promise 完成后,循环退出。
for await...of
通常与 for...of
循环的功能相同,并共享许多相同的语法和语义。存在一些差异
for await...of
在同步和异步可迭代对象上都起作用,而for...of
仅在同步可迭代对象上起作用。for await...of
只能在可以使用await
的上下文中使用,包括 异步函数 体和 模块 中。即使可迭代对象是同步的,循环仍然会等待每次迭代的返回值,由于重复的 Promise 解包导致执行速度变慢。- 如果
可迭代对象
是一个同步可迭代对象,它生成 Promise,那么for await...of
将生成已解析值的序列,而for...of
将生成 Promise 的序列。(但是,请注意错误处理和清理操作 - 请参阅 迭代同步可迭代对象和生成器) - 对于
for await...of
,变量
可以是标识符async
(例如for await (async of foo)
);for...of
禁止这种情况。
示例
迭代异步可迭代对象
您还可以迭代显式实现异步可迭代对象协议的对象
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
对它们进行循环。
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 的响应的大小。
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
循环还使用同步可迭代对象和生成器。在这种情况下,它会在内部等待发射的值,然后将其分配给循环控制变量。
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
释放一些分配的资源,这可能不理想。
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
- 并在循环内显式等待生成的值。
(async () => {
try {
for (const numOrPromise of generatorWithRejectedPromises()) {
console.log(await numOrPromise);
}
} catch (e) {
console.log("caught", e);
}
})();
// 0
// 1
// 2
// caught 3
// called finally
规范
规范 |
---|
ECMAScript 语言规范 # sec-for-in-and-for-of-statements |
浏览器兼容性
BCD 表格仅在浏览器中加载