asyncを使って非同期処理を制御2
前回の続きです。前回は非同期のtaskを順次または平行に処理を行う方法を書きましたが、今回は配列に特化した関数を紹介しようと思います。前回の記事の内容は理解していることを前提に書きます。
基礎編 配列の順次処理と並行処理
今回はasync.each
, async.eachLimit
, async.eachSeries
, async.map
, async.mapLimit
, async.mapSeries
の6つを説明したいと思います。6つもありますが関数名で察しがつく通り、覚えることは3つぐらいです。eachとmapの違いはArray.forEach
かArray.map
かぐらいの違いです。~Seriesがあると無いとの違いは順次処理か並行処理かの違いです。~Limitもそこまで難しいことはないです。
async.each(arr, iterator, callback)
第一引数に配列を渡し、第二引数にはiterator
を渡します。説明が下手なので例を見せます。
var async = require('async');
var list = [
{ id: 'first', delay: 200 },
{ id: 'two', delay: 100 },
{ id: 'three', delay: 150 },
];
async.each(list, function(data, next) {
setTimeout(function() {
console.log(data.id, 'done!!');
next();
}, data.delay);
}, function complete(err) {
console.log('all done!');
});
// --- output ---
// two done!!
// three done!!
// first done!!
// all done!
説明のために無名関数にcomplete
と名づけたり、setTimeout
の時間をずらしています。
async.each
はasync.parallel
のように並行処理です。Array.forEach
のようにiterator
の処理を実行します。iterator
の第二引数のnext
関数を呼ぶことで処理の完了を知らせます。配列の中身すべてのnext
が呼ばれるとcomplete
を実行します。説明下手ですがasync.parallel
を理解してればわかると思います。実行結果はdelay
の順番で表示されましたね。ちなみにasync.forEach
というaliasが用意されているのでお好きな方でお使いください。
async.eachLimit(arr, limit, iterator, callback)
引数にlimit
が増えました。実行結果を見てみましょう。
var async = require('async');
var list = [
{ id: 'first', delay: 200 },
{ id: 'two', delay: 100 },
{ id: 'three', delay: 150 },
];
async.eachLimit(list, 2, function(data, next) {
setTimeout(function() {
console.log(data.id, 'done!!');
next(null, data.id);
}, data.delay);
}, function(err) {
console.log('all done!');
});
// --- output ---
// two done!!
// first done!!
// three done!!
// all done!
実行結果が少し変わりましたね。基本的な処理の流れはasync.each
と同じです。ただし、async.each
はすべての配列データを並行処理していましたが、async.eachLimit
はlimit
分の処理しか同時に実行しません。今回の例でいうと2個同時にしか並行処理しないのでfirstとtwoが実行されて100ms後にtwoが終わったタイミングでthreeが開始します。threeが始まったことにはfirstのdelayは残り100msですのでthreeより先にfirstが出力されたのです。順序を気にせず並行処理をしてもよいが同時に走り過ぎると重たい非同期処理等があったときによく使われる関数です。aliasにasync.forEachLimit
があります。
async.eachSeries(arr, iterator, callback)
async.each
の順次処理版です。async.series
のように配列データを順次iterator
で実行していきます。
var async = require('async');
var list = [
{ id: 'first', delay: 200 },
{ id: 'two', delay: 100 },
{ id: 'three', delay: 150 },
];
async.eachSeries(list, function(data, next) {
setTimeout(function() {
console.log(data.id, 'done!!');
next();
}, data.delay);
}, function(err) {
console.log('all done!');
});
// --- output ---
// first done!!
// two done!!
// three done!!
// all done!
実行結果を見ての通りです。僕は割と使います。特に順序が重要じゃない処理もなんとなくasync.eachSeries
使います。なんとなくasync.eachLimit
は使いません。多分世間的にはasync.eachLimit
の方が利便性ありそうですがw aliasにasync.forEachSeries
があります。
async.map(arr, iterator, callback)
async.each
がArray.forEach
となればasync.map
はArray.map
です。callback関数の第二引数に値が返ってきます。
var async = require('async');
var list = [
{ id: 'first', delay: 200 },
{ id: 'two', delay: 100 },
{ id: 'three', delay: 150 },
];
async.map(list, function(data, next) {
setTimeout(function() {
console.log(data.id, 'done!!');
next(null, data.id);
}, data.delay);
}, function(err, results) {
console.log(JSON.stringify(results));
});
// --- output ---
// two done!!
// three done!!
// first done!!
// ["first","two","three"]
処理の流れはasync.each
と同じです。results
にデータが返ってきますが実行結果順ではなくてlist
と同じ添字で入っていきます。この辺はasync.parallel
等と同じです。
async.mapLimit(arr, limit, iterator, callback)
もう説明は特に必要ないと思いますが例だけ載せておきます。
var async = require('async');
var list = [
{ id: 'first', delay: 200 },
{ id: 'two', delay: 100 },
{ id: 'three', delay: 150 },
];
async.mapLimit(list, 2, function(data, next) {
setTimeout(function() {
console.log(data.id, 'done!!');
next(null, data.id);
}, data.delay);
}, function(err, results) {
console.log(JSON.stringify(results));
});
// --- output ---
// two done!!
// first done!!
// three done!!
// ["first","two","three"]
async.mapSeries(arr, iterator, callback)
同様にasync.eachSeries
のmap版です。
var async = require('async');
var list = [
{ id: 'first', delay: 200 },
{ id: 'two', delay: 100 },
{ id: 'three', delay: 150 },
];
async.mapSeries(list, function(data, next) {
setTimeout(function() {
console.log(data.id, 'done!!');
next(null, data.id);
}, data.delay);
}, function(err, results) {
console.log(JSON.stringify(results));
});
// --- output ---
// first done!!
// two done!!
// three done!!
// ["first","two","three"]
asyncの基礎となる関数の説明は以上です。年末に書いたのに上げるのがだいぶ遅くなった(汗