APIリクエストなど非同期処理をfor文で何度か繰り返し、それぞれの完了を待ってから次に進みたい時に。
例えば、こんなfor文を書いた時に、
var sum = 0
for (let i = 0; i <= 10; i++) {
exampleFunc(i).then(res => sum += res)
}
console.log(sum)
function exampleFunc(index) {
return new Promise((resolve, reject) => {
// 何か重い処理をして、resultを返す
resolve(index)
})
}このまま動かすと、for文は「i=1の時、exampleFunc実行→レスポンスを待たずに次へ→i=2の時、exampleFunc実行→…」という動作をするため、
console.log(sum)で表示される値が不安定(APIのレスポンス速度によって、集計されるタイミングがバラバラになる)
正常な動作として、「i=1の時、exampleFunc実行→レスポンスを受け取る→i=2の時、exampleFunc実行→レスポンスを受け取る→…」となってほしい。
方法1: async/awaitで確実に1件ずつ待つ(推奨)
最もシンプルで意図通りの動作をするのは、for...ofとawaitを組み合わせる方法です。
async function main() {
var sum = 0
for (let i = 0; i <= 10; i++) {
const res = await exampleFunc(i)
sum += res
}
console.log(sum)
}
function exampleFunc(index) {
return new Promise((resolve, reject) => {
// 何か重い処理をして、resultを返す
resolve(index)
})
}
main()awaitを使うと、そのPromiseがresolveされるまでfor文の次のループに進まないため、1件ずつ確実に完了を待ってから次を実行できます。通常の同期処理と同じ感覚で書けるのが利点です。
方法2: Promise.allで全件の完了を待つ(並列実行でよい場合)
「1件ずつ順番に」ではなく「全部同時に実行して、全て終わったら次に進みたい」場合は、Promise.allの方が高速です。
var tasks = []
var sum = 0
for (let i = 0; i <= 10; i++) {
tasks.push(exampleFunc(i).then(res => sum += res))
}
Promise.all(tasks).then(() => {
console.log(sum)
})
function exampleFunc(index) {
return new Promise((resolve, reject) => {
// 何か重い処理をして、resultを返す
resolve(index)
})
}Promise.allにPromiseの配列を渡すと、配列内の全てがresolveされ次第、then内が実行されます。for文の実行自体は待たずに全て同時に開始されるため、方法1(await)より高速ですが、「1件ずつ順番に」処理したい場合には向きません。
まとめ
- 1件ずつ順番に完了を待ちたい →
for...of+await(方法1) - 順番は問わず、全件終わったタイミングだけ知りたい →
Promise.all(方法2、より高速)


