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);
});
使用パッケージ
実行結果
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.
関連
