aboutsummaryrefslogtreecommitdiff
path: root/files/ja/web/javascript/reference/statements/for-await...of/index.html
blob: 4876cda80329125f4aaa44f187ff0602a8733120 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
---
title: for await...of
slug: Web/JavaScript/Reference/Statements/for-await...of
tags:
  - Iterate
  - Iteration
  - JavaScript
  - Reference
  - Statement
  - asynchronous
  - await
  - 文
  - 繰り返し
  - 非同期
translation_of: Web/JavaScript/Reference/Statements/for-await...of
---
<div>{{jsSidebar("Statements")}}</div>

<p><strong><code>for await...of</code></strong>は非同期および同期の<a href="/ja/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterable_protocol">反復可能オブジェクト</a>を使用して、反復処理を行うループを作成します。たとえば、組込みの {{jsxref("String")}}, {{jsxref("Array")}}, 配列風オブジェクト (例えば {{jsxref("Functions/arguments", "arguments")}}, {{domxref("NodeList")}} など), {{jsxref("TypedArray")}}, {{jsxref("Map")}}, {{jsxref("Set")}}, さらに、ユーザー定義の非同期・同期の反復可能オブジェクトを使用することができます。これはオブジェクトのそれぞれの識別可能なプロパティの値に対して、実行される文を表す独自の反復フックを呼び出します。 {{jsxref("Operators/await", "await")}} 演算子と同様に、この分は{{jsxref("Statements/async_function", "非同期関数", "", 1)}}の中でのみ使用されます。</p>

<div class="blockIndicator note">
<p><code>for await...of</code> は非同期の反復可能オブジェクトではない非同期イテレーターでは動作しません。</p>
</div>

<h2 id="Syntax" name="Syntax">構文</h2>

<pre class="syntaxbox notranslate">for await (<var>variable</var> of <var>iterable</var>) {
  <var>statement</var>
}
</pre>

<dl>
 <dt><code><var>variable</var></code></dt>
 <dd>反復処理の各回において、異なるプロパティの値が <code><var>variable</var></code> に割り当てられます。 <code><var>variable</var></code><code>const</code>, <code>let</code>, <code>var</code> で宣言することができます。</dd>
 <dt><code><var>iterable</var></code></dt>
 <dd>反復処理が行われる反復可能なプロパティを持つオブジェクトです。</dd>
</dl>

<h2 id="Examples" name="Examples"></h2>

<h3 id="Iterating_over_async_iterables" name="Iterating_over_async_iterables">非同期の反復可能オブジェクトでの繰り返し処理</h3>

<p>非同期反復可能プロトコルを明示的に実装しているオブジェクトを反復処理することができます。</p>

<pre class="brush:js notranslate">const asyncIterable = {
  [Symbol.asyncIterator]() {
    return {
      i: 0,
      next() {
        if (this.i &lt; 3) {
          return Promise.resolve({ value: this.i++, done: false });
        }

        return Promise.resolve({ done: true });
      }
    };
  }
};

(async function() {
   for await (let num of asyncIterable) {
     console.log(num);
   }
})();

// 0
// 1
// 2
</pre>

<h3 id="Iterating_over_async_generators" name="Iterating_over_async_generators">非同期のジェネレータの反復処理</h3>

<p>非同期の反復可能プロトコルを実装している非同期ジェネレーターであれば、 <code>for await...of</code> を使用して繰り返し処理を行うことができます。</p>

<pre class="brush: js notranslate">async function* asyncGenerator() {
  let i = 0;
  while (i &lt; 3) {
    yield i++;
  }
}

(async function() {
  for await (let num of asyncGenerator()) {
    console.log(num);
  }
})();
// 0
// 1
// 2</pre>

<p><code>for await...of</code> を使用して非同期ジェネレータを反復処理するより具体的な例として、API からのデータを反復処理することを考えてみましょう。</p>

<p>この例では、まずデータのストリームに対して非同期の反復可能オブジェクトを作成し、それを使用して API からのレスポンスの長さを計算します。</p>

<pre class="brush: js notranslate">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();
  }
}
// URL からデータを取得し、非同期ジェネレータを使用してレスポンスの長さを計算します。
async function getResponseSize(url) {
  const response = await fetch(url);
  // レスポンスの長さをバイト単位で保持する
  let responseSize = 0;
  // for-await-of ループ。レスポンスの各部分を非同期に反復処理します。
  for await (const chunk of streamAsyncIterable(response.body)) {
    // レスポンスの全長に加算
    responseSize += chunk.length;
  }

  console.log(`Response Size: ${responseSize} bytes`);
  // 期待される出力: "Response Size: 1071472"
  return responseSize;
}
getResponseSize('https://jsonplaceholder.typicode.com/photos');</pre>

<h3 id="Iterating_over_sync_iterables_and_generators" name="Iterating_over_sync_iterables_and_generators">同期の反復可能オブジェクトおよびジェネレーターの反復処理</h3>

<p><code>for await...of</code> ループは同期の反復可能オブジェクトやジェネレーターで使用することもできます。この場合、内部的にはループの制御変数に代入する前に、値が出力されるのを待ちます。</p>

<pre class="brush: js notranslate">function* generator() {
  yield 0;
  yield 1;
  yield Promise.resolve(2);
  yield Promise.resolve(3);
  yield 4;
}

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

// for-of ループとの比較:

for (let numOrPromise of generator()) {
  console.log(numOrPromise);
}
// 0
// 1
// Promise { 2 }
// Promise { 3 }
// 4
</pre>

<div></div>

<div class="blockIndicator note">
<p><strong></strong>: 同期のジェネレーターから拒否されたプロミスが生み出される場合があることに注意してください。このような場合、 <code>for await...of</code> は拒否されたプロミスを消費するので、ジェネレーター内の <code>finally</code> ブロックが呼び出されません。これは、確保したリソースを <code>try/finally</code> で解放する必要がある場合は望ましくない動作になる可能性があります。</p>
</div>

<pre class="brush: js notranslate">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 function() {
  try {
    for await (let num of generatorWithRejectedPromises()) {
      console.log(num);
    }
  } catch (e) {
    console.log('catched', e)
  }
})();
// 0
// 1
// 2
// catched 3

// for-of ループとの比較:

try {
  for (let numOrPromise of generatorWithRejectedPromises()) {
    console.log(numOrPromise);
  }
} catch (e) {
  console.log('catched', e)
}
// 0
// 1
// Promise { 2 }
// Promise { &lt;rejected&gt; 3 }
// 4
// catched 5
// called finally
</pre>

<p>同期ジェネレーター関数の <code>finally</code> ブロックが常に呼び出されるようにするには、非同期のジェネレーター関数の場合は <code>for await...of</code> を、同期ジェネレーター関数の場合は <code>for...of</code> を使用し、ループの中で生成されたプロミスを明示的に待つようにしてください。</p>

<pre class="brush: js notranslate">(async function() {
  try {
    for (let numOrPromise of generatorWithRejectedPromises()) {
      console.log(await numOrPromise);
    }
  } catch (e) {
    console.log('catched', e)
  }
})()
// 0
// 1
// 2
// catched 3
// called finally</pre>

<h2 id="Specifications" name="Specifications">仕様書</h2>

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">仕様書</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>{{SpecName('ESDraft', '#sec-for-in-and-for-of-statements', 'ECMAScript Language: The for-in, for-of, and for-await-of Statements')}}</td>
  </tr>
 </tbody>
</table>

<h2 id="Browser_compatibility" name="Browser_compatibility">ブラウザーの互換性</h2>

<p>{{Compat("javascript.statements.for_await_of")}}</p>

<h2 id="See_also" name="See_also">関連情報</h2>

<ul>
 <li>{{jsxref("Global_Objects/Symbol/asyncIterator", "Symbol.asyncIterator")}}</li>
 <li>{{jsxref("Statements/for...of", "for...of")}}</li>
</ul>