情報アイランド

「情報を制する者は世界を制す」をモットーに様々な情報を提供することを目指すブログです。現在はプログラミング関連情報が多めですが、投資関連情報も取り扱っていきたいです。

Node.jsで複数のプロミスを同期的に実行する

複数のプロミスを同期的に実行するには下のようなモジュールを利用する方法があります。

  • co
  • aa
  • bluebird
  • q

実行

複数のプロミスを同期的に実行するにはco関数かaa関数かbluebird.coroutine関数かq.spawn関数を使用します。

var co = require('co');

var p = co(function* () {
    var result1 = yield p1;
    var result3 = yield [p2, p3];
    var result4 = yield { p4: p4, p5: p5 };
    return 'aaa';
});
var aa = require('aa');

var p = aa(function* () {
    var result1 = yield p1;
    var result3 = yield [p2, p3];
    var result4 = yield { p4: p4, p5: p5 };
    return 'aaa';
});
var bluebird = require('bluebird');

var p = bluebird.coroutine(function* () {
    var result1 = yield p1;
    var result3 = yield [p2, p3];
    var result4 = yield { p4: p4, p5: p5 };
    return 'aaa';
});
var q = require('q');

var p = q.spawn(function* () {
    var result1 = yield p1;
    var result3 = yield [p2, p3];
    var result4 = yield { p4: p4, p5: p5 };
    return 'aaa';
});

第1引数にジェネレータを指定します。

このジェネレータの本体において複数のプロミスを作成し、プロミスの処理を実行します。

ただし、プロミスはyield文の値として指定します

これにより、プロミスの処理が完了するまでyield文の評価は待機されます。そのため、プロミスの処理が完了するまでyield文より後の処理が実行されることはありません。

すなわち、同期的な処理が実現されます

あるいは、yield文の値としてはプロミスの配列やプロミスをプロパティの値とするオブジェクトを指定することもできます。

この場合、配列やオブジェクトの全てのプロミスの処理が完了するまでyield文の評価は待機されます

すなわち、全体では同期的な処理を行いながら一部では非同期的な処理を行うこともできます

返り値として複数のプロミスを同期的に実行するプロミス(co関数とaa関数の場合にはECMAScript 6で追加されたPromiseクラスのインスタンス、bluebird.coroutine関数の場合にはbluebirdクラスのインスタンス、q.spawn関数の場合にはq.Promiseクラスのインスタンス)が得られます。

このプロミスの結果は第1引数のジェネレータの返り値です。

関数の取得

複数のプロミスを同期的に実行する関数を取得するにはco.wrap関数を使用します。

var f = co.wrap(function* (arg1, arg2) {
    var result1 = yield p1;
    var result3 = yield [p2, p3];
    var result4 = yield { p4: p4, p5: p5 };
    return 'aaa';
});
var p = f('xxx', 'yyy');

第1引数はco関数と同じです。ただし、ジェネレータには引数を定義することができます。

返り値として複数のプロミスを同期的に実行する関数が得られます。この関数の引数は第1引数のジェネレータの引数と同じです。また、この関数の返り値は複数のプロミスを同期的に実行するプロミスです。

このプロミスの結果は第1引数のジェネレータの返り値です。

サンプルコード1

co関数の使用例です。

co.js

var co = require('co');
var promise = require('promise');

if (process.argv.length < 3) {
    console.error('lack argument.');
    process.exit(1);
}

var p = co(function* () {
    var result1 = yield new Promise(function (resolve, reject) {
        var n = Number(process.argv[2]);
        if (n || n === 0) {
            resolve(n);
        }
        else {
            throw new Error('not a number.');
        }
    });
    var result2 = yield new Promise(function (resolve, reject) {
        if (result1 % 2 === 0) {
            resolve(result1 / 2);
        }
        else {
            throw new Error('not divisible by 2.');
        }
    });
    var result3 = yield new Promise(function (resolve, reject) {
        if (result2 % 3 === 0) {
            resolve(result2 / 3);
        }
        else {
            throw new Error('not divisible by 3.');
        }
    });
    return result3;
});

promise.resolve(p).done(function (result) {
    console.log('result is ' + result + '.');
}, function (err) {
    console.error(err);
    process.exit(1);
});

使用パッケージ

  • co
    npm install coでインストールします。
  • promise
    npm install promiseでインストールします。

実行結果

C:\work\node>node co.js 0
result is 0.

C:\work\node>node co.js 2
Error: not divisible by 3.
    at result3 (C:\work\node\co.js:32:10)
    at C:\work\node\co.js:27:22
    at next (native)
    at onFulfilled (C:\work\node\node_modules\co\index.js:65:19)
    at process._tickCallback (internal/process/next_tick.js:103:7)
    at Function.Module.runMain (module.js:577:11)
    at startup (node.js:160:18)
    at node.js:449:3

C:\work\node>node co.js 3
Error: not divisible by 2.
    at result2 (C:\work\node\co.js:24:10)
    at C:\work\node\co.js:19:22
    at next (native)
    at onFulfilled (C:\work\node\node_modules\co\index.js:65:19)
    at process._tickCallback (internal/process/next_tick.js:103:7)
    at Function.Module.runMain (module.js:577:11)
    at startup (node.js:160:18)
    at node.js:449:3

C:\work\node>node co.js 6
result is 1.

関連

pizyumi
プログラミング歴19年のベテランプログラマー。業務システム全般何でも作れます。現在はWeb系の技術を勉強中。
スポンサーリンク

-Node.js