LoginSignup
4
5

More than 5 years have passed since last update.

promiseの順次実行(reduce,co+generator,async/await)

Last updated at Posted at 2016-03-28

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に備えるのがよいと思います。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5