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);