In node.js ist nichts wirklich parallel, da es sich um Single-Threaded handelt. Es können jedoch mehrere Ereignisse geplant und in einer Reihenfolge ausgeführt werden, die Sie nicht im Voraus bestimmen können. Und einige Dinge wie der Datenbankzugriff sind tatsächlich "parallel", da die Datenbankabfragen selbst in separaten Threads ausgeführt werden, aber nach Abschluss wieder in den Ereignisstrom integriert werden.
Wie planen Sie einen Rückruf für mehrere Ereignishandler? Nun, dies ist eine gängige Technik, die in Animationen in browser-seitigem Javascript verwendet wird: Verwenden Sie eine Variable, um den Abschluss zu verfolgen.
Dies klingt wie ein Hack und ist es auch. Es klingt möglicherweise chaotisch und hinterlässt eine Reihe globaler Variablen, um das Tracking durchzuführen, und in einer geringeren Sprache wäre dies der Fall. Aber in Javascript können wir Verschlüsse verwenden:
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);
}
}
fork([A,B,C],D);
Im obigen Beispiel halten wir den Code einfach, indem wir davon ausgehen, dass die Async- und Callback-Funktionen keine Argumente erfordern. Sie können den Code natürlich so ändern, dass Argumente an die asynchronen Funktionen übergeben werden und die Rückruffunktion Ergebnisse sammelt und an die Funktion shared_callback übergibt.
Zusätzliche Antwort:
Tatsächlich fork()
kann diese Funktion , so wie sie ist, bereits Argumente über einen Abschluss an die asynchronen Funktionen übergeben:
fork([
function(callback){ A(1,2,callback) },
function(callback){ B(1,callback) },
function(callback){ C(1,2,callback) }
],D);
Sie müssen nur noch die Ergebnisse von A, B, C akkumulieren und an D weitergeben.
Noch mehr zusätzliche Antwort:
Ich konnte nicht widerstehen. Ich habe beim Frühstück darüber nachgedacht. Hier ist eine Implementierung fork()
, die Ergebnisse sammelt (normalerweise als Argumente an die Rückruffunktion übergeben):
function fork (async_calls, shared_callback) {
var counter = async_calls.length;
var all_results = [];
function makeCallback (index) {
return function () {
counter --;
var results = [];
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));
}
}
Das war einfach genug. Dies ist ein fork()
ziemlich allgemeiner Zweck und kann verwendet werden, um mehrere inhomogene Ereignisse zu synchronisieren.
Beispiel für die Verwendung in Node.js:
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];
}
fork([A,B,C],D);
Aktualisieren
Dieser Code wurde vor der Existenz von Bibliotheken wie async.js oder den verschiedenen vielversprechenden Bibliotheken geschrieben. Ich würde gerne glauben, dass async.js davon inspiriert wurde, aber ich habe keinen Beweis dafür. Wie auch immer ... wenn Sie heute daran denken, werfen Sie einen Blick auf async.js oder Versprechen. Betrachten Sie die obige Antwort als eine gute Erklärung / Illustration dafür, wie Dinge wie async.parallel funktionieren.
Der Vollständigkeit halber würden Sie Folgendes tun async.parallel
:
var async = require('async');
async.parallel([A,B,C],D);
Beachten Sie, dass async.parallel
dies genauso fork
funktioniert wie die oben implementierte Funktion. Der Hauptunterschied besteht darin, dass ein Fehler als erstes Argument an D
und der Rückruf als zweites Argument gemäß der Konvention node.js übergeben wird.
Mit Versprechungen würden wir es wie folgt schreiben:
Promise.all([A,B,C]).then(D);
Ich glaube, dass das "asynchrone" Modul jetzt diese parallele Funktionalität bietet und ungefähr der oben genannten Gabelfunktion entspricht.
quelle
fork
FunktionDas Futures- Modul hat ein Submodul namens Join , das ich gerne verwendet habe:
Die Readme-Datei zeigt einige gute Beispiele für die Verwendung im Freestyle oder für die Verwendung des zukünftigen Submoduls mithilfe des Promise-Musters. Beispiel aus den Dokumenten:
var Join = require('join') , join = Join() , callbackA = join.add() , callbackB = join.add() , callbackC = join.add(); function abcComplete(aArgs, bArgs, cArgs) { console.log(aArgs[1] + bArgs[1] + cArgs[1]); } setTimeout(function () { callbackA(null, 'Hello'); }, 300); setTimeout(function () { callbackB(null, 'World'); }, 500); setTimeout(function () { callbackC(null, '!'); }, 400); // this must be called after all join.when(abcComplete);
quelle
Hier könnte eine einfache Lösung möglich sein: http://howtonode.org/control-flow-part-ii Scrollen Sie zu Parallele Aktionen. Eine andere Möglichkeit wäre, A, B und C alle dieselbe Rückruffunktion zu haben, diese Funktion einen globalen oder zumindest funktionsunabhängigen Inkrementor zu haben. Wenn alle drei den Rückruf aufgerufen haben, lassen Sie ihn D ausführen. Natürlich müssen Sie die Ergebnisse von A, B und C auch irgendwo speichern.
quelle
Eine weitere Option könnte das Step-Modul für Node sein: https://github.com/creationix/step
quelle
Vielleicht möchten Sie diese winzige Bibliothek ausprobieren: https://www.npmjs.com/package/parallel-io
quelle
Zusätzlich zu den beliebten Versprechungen und der Async-Bibliothek gibt es einen dritten eleganten Weg - mit "Verkabelung":
var l = new Wire(); funcA(l.branch('post')); funcB(l.branch('comments')); funcC(l.branch('links')); l.success(function(results) { // result will be object with results: // { post: ..., comments: ..., links: ...} });
https://github.com/garmoshka-mo/mo-wire
quelle