#経緯
forループ内で同期処理を行いたかったので調べてみたら for await...of というものがあることを知りました。
#結論
このように async
のなかに for await...of
を書くことで forループ内で await
を宣言できます。
// 対象の反復オブジェクト
const targetArr = [1, 2, 3];
// 実行する関数
const sampleFunc = (value) => {
// asyncの効果は各functionブロックで切れるので逐一指定が必要
return new Promise(resolve => {
// 2秒待ってから計算結果をresolveする
setTimeout(() => {
console.log('Calculating...');
resolve(value * 2);
}, 2000);
})
}
// for await...of文は必ずasyncの中で
(async () => {
for await (num of targetArr) {
// 関数の実行結果を格納して表示
const result = await sampleFunc(num);
console.log(result);
}
})();
#for await...of とは
for await...of 文は非同期(と同期)の反復オブジェクトを繰り返して処理するループを作ります。対象の反復オブジェクトは、ビルトインの String、Array、配列様オブジェクト( arguments、NodeList 等)、TypedArray、Map、Set、さらに、ユーザーが定義した非同期・同期の反復オブジェクトが含まれます。オブジェクトの各プロパティの値に対して実行されるステートメントを使用してカスタム反復フックを呼び出します。
簡単に言うと
反復オブジェクト(ArrayやObjectなど)の中で同期処理を行う事ができる文です。
#ESLintでは非推奨
便利な構文ですが、ESLintでは設計思想的な意味で推奨されていません。
https://eslint.org/docs/rules/no-await-in-loop
Performing an operation on each element of an iterable is a common task. However, performing an await as part of each operation is an indication that the program is not taking full advantage of the parallelization benefits of async/await.
Usually, the code should be refactored to create all the promises at once, then get access to the results using Promise.all(). Otherwise, each successive operation will not start until the previous one has completed.
Concretely, the following function should be refactored as shown:
反復の各要素に対して操作を行うことは一般的な作業です。
しかしながら、各段階の操作で await
を実行すると async/await
による並列化の利点を十分に活用できません。
一般にこのようなコードは、全てのプロミスを一度に作成し Promise.all()
を用いて結果を得るようにするべきです。