Skip to content

Commit

Permalink
[zh-cn]: update the translation of for-await...of... in statement sec…
Browse files Browse the repository at this point in the history
…tion (#18769)

Co-authored-by: Jason Ren <40999116+jasonren0403@users.noreply.github.com>
  • Loading branch information
JasonLamv-t and jasonren0403 authored Mar 19, 2024
1 parent a4df25d commit fcdcc76
Showing 1 changed file with 158 additions and 38 deletions.
196 changes: 158 additions & 38 deletions files/zh-cn/web/javascript/reference/statements/for-await...of/index.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,93 @@
---
title: for await...of
slug: Web/JavaScript/Reference/Statements/for-await...of
l10n:
sourceCommit: fb85334ffa4a2c88d209b1074909bee0e0abd57a
---

{{jsSidebar("Statements")}}

**`for await...of` 语句**创建一个循环,该循环遍历异步可迭代对象以及同步可迭代对象,包括:内置的 {{jsxref("String")}}, {{jsxref("Array")}},类似数组对象 (例如 {{jsxref("Functions/arguments", "arguments")}} 或 {{DOMxRef("NodeList")}}),{{jsxref("TypedArray")}}, {{jsxref("Map")}}, {{jsxref("Set")}} 和用户定义的异步/同步迭代器。它使用对象的每个不同属性的值调用要执行的语句来调用自定义迭代钩子
**`for await...of`** 语句创建一个循环,该循环遍历[异步可迭代对象](/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#异步迭代器和异步可迭代协议)以及[同步可迭代对象](/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#可迭代协议)。该语句只能在可以使用 [`await`](/zh-CN/docs/Web/JavaScript/Reference/Operators/await) 的上下文中使用,包括[异步函数](/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function)体内以及[模块](/zh-CN/docs/Web/JavaScript/Guide/Modules)

类似于 {{jsxref("Operators/await", "await")}} 运算符一样,该语句只能在一个{{jsxref("Statements/async_function", "async function", "异步函数", 1)}} 内部使用。

> **备注:** `for await...of` 不适用于不是异步可迭代的异步迭代器。
{{EmbedInteractiveExample("pages/js/statement-forawaitof.html", "taller")}}

## 语法

```plain
for await (variable of iterable) {
```js-nolint
for await (variable of iterable)
statement
}
```

- `variable`
- : 在每次迭代中,将不同属性的值分配给变量。变量有可能以`const`, `let`, 或者 `var`来声明
- : 每次迭代时从序列接收一个值。可以是用 [`const`](/zh-CN/docs/Web/JavaScript/Reference/Statements/const)[`let`](/zh-CN/docs/Web/JavaScript/Reference/Statements/let)[`var`](/zh-CN/docs/Web/JavaScript/Reference/Statements/var) 声明的变量,也可以是[赋值](/zh-CN/docs/Web/JavaScript/Reference/Operators/Assignment)目标(例如之前声明的变量、对象属性或[解构赋值模式](/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment))。使用 `var` 声明的变量不会局限于循环内部,即它们与 `for await...of` 循环所在的作用域相同
- `iterable`
- : 被迭代枚举其属性的对象。与 for...of 相比,这里的对象可以返回 `Promise`,如果是这样,那么 `variable` 将是 `Promise` 所包含的值,否则是值本身。
- : 异步可迭代对象或同步可迭代对象。循环操作的序列值的来源。
- `statement`
- : 每次迭代后执行的语句。可以引用 `variable`。可以使用[块语句](/zh-CN/docs/Web/JavaScript/Reference/Statements/block)来执行多个语句。

## 描述

当 `for await...of` 循环迭代一个可迭代对象时,它首先获取可迭代对象的 [`[@@asyncIterator]()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator) 方法并调用它,该方法返回一个[异步迭代器](/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#异步迭代器和异步可迭代协议)。如果 `@asyncIterator` 方法不存在,则会查找 [`[@@iterator]()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) 方法,该方法返回一个[同步迭代器](/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#迭代器协议)。然后将返回的同步迭代器封装成一个异步迭代器,通过将 `next()`、`return()` 和 `throw()` 方法返回的每个对象都包装成一个已兑现或已拒绝的 promise,如果 `value` 属性也是一个 promise,则将其兑现。然后循环重复调用最终异步迭代器的 [`next()`](/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#迭代器协议) 方法,并[等待](/zh-CN/docs/Web/JavaScript/Reference/Operators/await)(await)返回的 promise,以生成要分配给 `variable` 的值的序列。

如果 `for await...of` 循环提前退出(例如遇到 `break` 语句或抛出错误),则会调用迭代器的 [`return()`](/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#迭代器协议) 方法来执行任何清理任务。在循环退出之前,会等待返回的 promise。

`for await...of` 的功能与 [`for...of`](/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of) 循环基本上相同,并且共享许多相同的语法和语义。但也有一些区别:

- `for await...of` 可以用于同步和异步可迭代对象,而 `for...of` 仅适用于同步可迭代对象。
- `for await...of` 只能在可以使用 [`await`](/zh-CN/docs/Web/JavaScript/Reference/Operators/await) 的上下文中使用,包括在[异步函数](/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function)体内和[模块](/zh-CN/docs/Web/JavaScript/Guide/Modules)中使用。即使可迭代对象是同步的,循环仍会等待每次迭代的返回值,导致执行速度较慢,因为需要重复解包 promise。
- 如果 `iterable` 是一个产生 promise 的同步可迭代对象,`for await...of` 会生成一个已兑现的值序列,而 `for...of` 会生成一个 promise 序列。(然而,需要注意错误处理和清理——请参阅[迭代同步可迭代对象和生成器](#迭代同步可迭代对象和生成器)
- 对于 `for await...of``variable` 可以是标识符 `async`(例如 `for await (async of foo)`);`for...of` 不允许这种情况。

## 示例

### 迭代异步可迭代对象

你还可以迭代一个明确实现异步迭代协议的对象
你还可以迭代一个显式实现异步可迭代协议的对象

```js
var asyncIterable = {
const LIMIT = 3;

const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
i: 0,
next() {
if (this.i < 3) {
return Promise.resolve({ value: this.i++, done: false });
}

return Promise.resolve({ done: true });
const done = i === LIMIT;
const value = done ? undefined : i++;
return Promise.resolve({ value, done });
},
return() {
// 如果消费者在循环中提前调用“break”或“return”,则会运行到这里。
return { done: true };
},
};
},
};

(async function () {
for await (num of asyncIterable) {
(async () => {
for await (const num of asyncIterable) {
console.log(num);
}
})();

// 0
// 1
// 2
```

### 迭代异步生成器

异步生成器已经实现了异步迭代器协议,所以可以用 `for await...of`循环
由于异步生成器函数的返回值符合异步可迭代协议,因此可以使用 `for await...of` 来迭代它们

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

(async function () {
for await (num of asyncGenerator()) {
(async () => {
for await (const num of asyncGenerator()) {
console.log(num);
}
})();
Expand All @@ -79,41 +96,144 @@ async function* asyncGenerator() {
// 2
```

有关使用`for await... of`考虑迭代 API 中获取数据的异步 generator 更具体的例子。这个例子首先为一个数据流创建了一个异步 generator,然后使用它来获得这个 API 的响应值的大小。
如果需要使用 `for await...of` 迭代异步生成器的更具体示例时,可以考虑迭代来自 API 的数据。

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

```js
async function* streamAsyncIterator(stream) {
async function* streamAsyncIterable(stream) {
const reader = stream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
return;
}
if (done) return;
yield value;
}
} finally {
reader.releaseLock();
}
}
// 从 url 获取数据并使用异步 generator 来计算响应值的大小

// 从 URL 获取数据并使用异步生成器计算响应的大小。
async function getResponseSize(url) {
const response = await fetch(url);
// Will hold the size of the response, in bytes.
// 将保存响应的大小,以字节为单位。
let responseSize = 0;
// 使用 for-await-of 循环。异步 generator 会遍历响应值的每一部分
for await (const chunk of streamAsyncIterator(response.body)) {
// Incrementing the total response length.
// for-await-of 循环。异步迭代器遍历响应的每个部分。
for await (const chunk of streamAsyncIterable(response.body)) {
// 增加响应总长度。
responseSize += chunk.length;
}

console.log(`Response Size: ${responseSize} bytes`);
// expected output: "Response Size: 1071472"
console.log(`响应大小:${responseSize} 字节`); // "响应大小: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

// 与 for-of 循环比较:

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

> **备注:** 请注意,如果同步生成器生成了一个被拒绝的 promise,那么在使用 `for await...of` 进行处理时会抛出异常,并且**不会调用**生成器内部的 `finally` 块。如果你需要使用 `try/finally` 来释放一些已分配的资源,这可能是不可取的。
```js
function* generatorWithRejectedPromises() {
try {
yield 0;
yield 1;
yield Promise.resolve(2);
yield Promise.reject(3);
yield 4;
throw 5;
} finally {
console.log("调用了 finally");
}
}

(async () => {
try {
for await (const num of generatorWithRejectedPromises()) {
console.log(num);
}
} catch (e) {
console.log("捕获了异常", e);
}
})();
// 0
// 1
// 2
// 捕获了异常 3

// 与 for-of 循环比较:

try {
for (const numOrPromise of generatorWithRejectedPromises()) {
console.log(numOrPromise);
}
} catch (e) {
console.log("捕获了异常", e);
}
// 0
// 1
// Promise { 2 }
// Promise { <rejected> 3 }
// 4
// 捕获了异常 5
// 调用了 finally
```

为了确保同步生成器的 `finally` 块始终被调用,需要使用对应的循环形式——对于异步生成器使用 `for await...of`,对于同步生成器则使用 `for...of`,并在循环内显式地等待生成的 promise。

```js
(async () => {
try {
for (const numOrPromise of generatorWithRejectedPromises()) {
console.log(await numOrPromise);
}
} catch (e) {
console.log("捕获了异常", e);
}
})();
// 0
// 1
// 2
// 捕获了异常 3
// 调用了 finally
```

## 规范

{{Specifications}}
Expand All @@ -124,5 +244,5 @@ getResponseSize("https://jsonplaceholder.typicode.com/photos");

## 参见

- {{jsxref("Global_Objects/Symbol/asyncIterator", "Symbol.asyncIterator")}}
- {{jsxref("Statements/for...of")}}
- {{jsxref("Symbol.asyncIterator")}}
- {{jsxref("Statements/for...of", "for...of")}}

0 comments on commit fcdcc76

Please sign in to comment.