※ async.js が嫌いな訳ではありません。今までお世話になっていました。
Node.js v0.12 から Promise が標準で使えるようになったので async.js の parallel
や series
で行っていたことを Promise
で実現しよう!という趣旨です。
parallel (並列実行)
var count = 0;
function echo() {
var cnt = count++;
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(cnt);
resolve(cnt);
}, Math.random() * 1000);
});
}
var promises = [echo(), echo(), echo()];
Promise.all(promises).then(function (values) {
// 成功時処理 (実行順に関わらず values は [0, 1, 2])
console.log("Succeeded!", values);
}).catch(function (err) {
// 例外処理
console.error("Failed.", err);
});
シングルスレッドなので正確には並列ではありませんが、処理が終了したものから順に出力されるため、実行する毎に表示される数字の順番が変わります。
series (直列実行)
var count = 0;
function echo() {
var cnt = count++;
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(cnt);
resolve(cnt);
}, Math.random() * 1000);
});
}
var tasks = [echo, echo, echo];
tasks.reduce(function (a, b) {
return a.then(b);
}, Promise.resolve(null)).then(function () {
// 成功時処理
console.log("Succeeded!");
}).catch(function (err) {
// 例外処理
console.error("Failed.", err);
});
こちらは、必ず 0, 1, 2 の順に処理が走ります。
前の処理が終わるまで次の関数を評価しない為です。
まとめ
Promise
はインスタンスが生成された時点で渡された関数を評価するため、直列実行したい場合は関数でラップする必要があります。
Array#reduce
メソッドを利用することで、複数の Promise
を返す関数を纏めて 1 つの Promise
に集約することが出来ます。
Array#reduce
の第 2 引数に初期値として Promise
を渡します。このように記述すると、 tasks
配列が空の場合でも正しく処理が進みます。
直列実行の際に値を配列で受け取りたい
tasks.reduce(function (a, b) {
return a.then(function (values) {
return b().then(function (value) {
return Promise.resolve(values.concat([value]));
});
});
}, Promise.resolve([])).then(function (values) {
// 成功時処理
console.log("Succeeded!", values);
}).catch(function (err) {
// 例外処理
console.error("Failed.", err);
});
そんな需要もあるかと思い、書いておきます。
reduce の中が複雑になりますが、このように記述することで echo 関数に変更を加えずに要件を満たせます。