JavaScript

JavaScriptでループの中で非同期処理を書く

for文やmap関数の中でasync/awaitを使うときの書き方をメモします。

各非同期処理を逐次的に実行する方法と、非同期的に実行する方法の2つがあります。


1. 同期的に実行

キャプチャ2.gif


2. 非同期的に実行

キャプチャ2.gif

awaitを使いたい非同期処理は以下


sleep.js

// 引数に指定した秒数だけ待つsleep関数

// Promiseを返す
function sleep(second) {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, second * 1000)
})
}


1. 同期的に実行する方法

for文の中でawaitを使って書くと、forループの中身を順番に処理してくれるので、同期的に実行してくれます。ここではカウンタを使ったfor文を書いていますが、for .. of文を使っても同じ挙動になります。


syncLoop.js

async function syncLoop() {

// 1~5までのランダムな整数の配列を生成
const ran_arr = Array(5).fill(0).map(() => Math.floor(Math.random() * 5 + 1))

// for文を使ってループ処理する
for (let i = 0; i < ran_arr.length; i++) {
await sleep(ran_arr[i]) // n秒待つ
console.log(i+1 + "番目の処理が完了!!" + "待ち時間は" + ran_arr[i] + "秒!!")
}
console.log("終了!!")
}

// 実行
// awaitはトップレベルで使うことができない(ES2017時点)ので
// asyncをつけた即時関数を作って呼び出している
(async ()=>{
console.log("同期的に呼び出す")
await syncLoop()
}).call()



2. 非同期的に実行する方法

mapを使います。mapの引数に指定する関数をasync関数にしておくことで、mapの中で呼ばれる処理を非同期に実行しつつ、mapの返り値であるPromise<void>[]Promise.all()関数で待ち受けることで、 すべての処理の完了を待つことができます。


asyncLoop.js

async function asyncLoop() {

// 1~5までのランダムな整数の配列を生成
const ran_arr = Array(5).fill(0).map(() => Math.floor(Math.random() * 5 + 1))

// mapを使ってループ処理する
await Promise.all(ran_arr.map(async (n, i) => {
await sleep(n) // n秒待つ
console.log(i+1 + "番目の処理が完了!!" + "待ち時間は" + n + "秒!!")
return
}))
console.log("終了!!")
}

// 実行
// awaitはトップレベルで使うことができない(ES2017時点)ので
// asyncをつけた即時関数を作って呼び出している
(async ()=>{
console.log("非同期的に呼び出す")
await asyncLoop()
}).call()


1番目の方法は次のループの処理をブロッキングしてしまうので、基本的には2番目の方法を使っておいて、どうしても非同期処理の実行順序を制御したいときのみ1番目の方法を使うのが、性能的な面で良いと思います。

以上