Async.jsで繰り返し処理
satkakuです。Async.jsの繰り返し処理で少しはまったので、それについてのメモです。
Async.jsは非同期処理のフロー制御をしてくれるライブラリで、順番に処理を書けるseriesや、前回の処理結果を次の処理に渡せるwaterfallなどがあります(以前の記事)。
そんなAsync.jsには、配列を非同期で処理するために便利なforEachがあります。
# IDが配列に入っているとする idList = [ "1", "2", "3" ] # IDに紐づくデータを非同期で取得する async.forEach(idList, (val, next)=> repository.findByID(val, (err, res)=> # 取得結果を別の配列に入れる @result.push(res) next() ) , -> # 配列の全ての結果が返ってきた際に呼ばれる done() )
こんな感じでやると、簡単に配列内のデータについて全て非同期で処理できます。
……しかし実際にこの処理を動かすと、resultという配列に入る結果は[ "1", "2", "3" ]の順序が保たれていません。何故? となったところで気付きました。forEachに引数として渡す関数の引数名を「next」としており、さも配列が順番に処理されるっぽく書いていますが、これは自分で勝手につけた名前です。ここは「callback」という名前にすべきでしょう。あくまで、各非同期処理の終了を告げるためのもののようです。
結論から言うと、forEachは順番を保証しないので、forEachSeriesを使います。
# IDが配列に入っているとする idList = [ "1", "2", "3" ] # IDに紐づくデータを非同期で取得する async.forEachSeries(idList, (val, callback)=> repository.findByID(val, (err, res)=> # 取得結果を別の配列に入れる @result.push(res) callback() ) , -> # 配列の全ての結果が返ってきた際に呼ばれる done() )
これで、resultの中身はidListの順番と同じになります。
・順番を保証したい場合はforEachSeriesを使う
今回の教訓は、実際の動きを分かっていないうちに、思い込みで変数名をつけると痛い目を見るということです。分かんなかったらソース読めという話ですね。まだソース読んでないので、改めてちゃんと読んでおこうと思いました。