node.js はシングル スレッドであるため、真の並列性はありません。ただし、事前に決定できない順序で複数のイベントをスケジュールして実行することができます。また、データベース アクセスなどの一部は、データベース クエリ自体が個別のスレッドで実行されますが、完了するとイベント ストリームに再統合されるという点で、実際には「並列」です。
では、複数のイベント ハンドラでコールバックをスケジュールするにはどうすればよいでしょうか。これは、ブラウザー側の JavaScript のアニメーションで使用される一般的な手法の 1 つです。変数を使用して完了を追跡します。
これはハックのように聞こえますが、ハックのように聞こえます。追跡を行う際に大量のグローバル変数を残しておくと、厄介な言語になる可能性があります。しかし、JavaScript ではクロージャーを使用できます。
function fork (async_calls, shared_callback) {
var counter = async_calls.length;
var callback = function () {
counter --;
if (counter == 0) {
shared_callback()
}
}
for (var i=0;i<async_calls.length;i++) {
async_calls[i](callback);
}
}
// usage:
fork([A,B,C],D);
上記の例では、async 関数と callback 関数が引数を必要としないと仮定して、コードを単純にしています。もちろん、コードを変更して引数を非同期関数に渡し、コールバック関数で結果を蓄積して shared_callback 関数に渡すようにすることもできます。
追加の回答:
実際、そのままでも、そのfork()
関数はクロージャーを使用して非同期関数に引数を渡すことができます。
fork([
function(callback){ A(1,2,callback) },
function(callback){ B(1,callback) },
function(callback){ C(1,2,callback) }
],D);
あとは、A、B、C からの結果を蓄積して D に渡すだけです。
さらに追加の答え:
私は抵抗できませんでした。朝食の間、これについて考え続けました。結果を蓄積する の実装を次にfork()
示します (通常はコールバック関数に引数として渡されます)。
function fork (async_calls, shared_callback) {
var counter = async_calls.length;
var all_results = [];
function makeCallback (index) {
return function () {
counter --;
var results = [];
// we use the arguments object here because some callbacks
// in Node pass in multiple arguments as result.
for (var i=0;i<arguments.length;i++) {
results.push(arguments[i]);
}
all_results[index] = results;
if (counter == 0) {
shared_callback(all_results);
}
}
}
for (var i=0;i<async_calls.length;i++) {
async_calls[i](makeCallback(i));
}
}
それはとても簡単でした。これはfork()
かなり汎用性が高く、複数の不均一なイベントを同期するために使用できます。
Node.js での使用例:
// Read 3 files in parallel and process them together:
function A (c){ fs.readFile('file1',c) };
function B (c){ fs.readFile('file2',c) };
function C (c){ fs.readFile('file3',c) };
function D (result) {
file1data = result[0][1];
file2data = result[1][1];
file3data = result[2][1];
// process the files together here
}
fork([A,B,C],D);
アップデート
このコードは、async.js やさまざまな promise ベースのライブラリなどのライブラリが存在する前に作成されました。async.js はこれに触発されたと信じたいのですが、その証拠はありません。とにかく..今日これを行うことを考えているなら、async.jsまたはpromiseを見てください。上記の回答を、async.parallel などの機能の適切な説明/図と考えてください。
完全を期すために、次のようにしますasync.parallel
。
var async = require('async');
async.parallel([A,B,C],D);
上記で実装async.parallel
した関数とまったく同じように機能することに注意してください。主な違いは、node.js の規則に従ってfork
、最初の引数としてエラーを渡し、 2 番目の引数としてコールバックを渡すことです。D
promise を使用して、次のように記述します。
// Assuming A, B & C return a promise instead of accepting a callback
Promise.all([A,B,C]).then(D);