promiseを並列処理する場合はPromise.all
すればいいですが、並列実行となると少し面倒です。
試しに、1秒ごとに一文字ずつ表示するプログラムを書いて試行錯誤してみます。
環境はnode v4以降とします。
reduce + Promise.resolve
Promiseをreduceで連結して処理する方法です。
const arr = ['a', 'b', 'c'];
function sleep() {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
}
arr.reduce((promise, v) => {
return promise
.then(() => console.log(v))
.then(sleep);
}, Promise.resolve());
promise.thenはreturnする値をpromise.resolveでラップして新しいPromiseオブジェクトを返します。
なので、以下のようにsleep関数をreturnをしない場合、sleep関数のPromiseの解決を待つことなく次の処理に進むので1秒おきに実行されません。
arr.reduce((promise, v) => {
return promise
.then(() => {
console.log(v);
sleep();
});
}, Promise.resolve());
1秒ごとに表示するためには、正しくpromiseをreturnする必要があります。
arr.reduce((promise, v) => {
return promise
.then(() => {
console.log(v);
return sleep();
});
}, Promise.resolve());
reduceを使った方法を見てきましたが、考える事が多くあまりスマートではないですね。
co + generator
coとgeneratorを使ってみます。
$ npm i -S co
sleepは上記の関数を使い回します。
coでジェネレーター関数をラップしてやります。
こちらのほうがずっとわかりやすいですね。
const co = require('co');
co(function *() {
for (const v of arr) {
console.log(v);
yield sleep();
}
});
また、coはthunkを引数にとれます。
なので、sleepを以下のように書くこともできます。
const sleep = ms => cb => setTimeout(cb, ms || 1000);
しかしながら、coがasync/awaitまでのつなぎと考えるとpromiseベースの関数を使ったほうがよいと思います。
おまけ (async/await)
そもそもasync/awaitが使える環境であれば、forで回して終わりなんで特に考えることはないですね。以下、babel環境で試してください。
(async () => {
for (const v of arr) {
console.log(v);
await sleep();
}
})();
まとめ
co
を使い、async/awaitに備えるのがよいと思います。