Ответ 1
Ничто не является действительно параллельным в node.js, поскольку оно однопоточное. Однако несколько событий можно планировать и запускать в последовательности, которую вы не можете определить заранее. И некоторые вещи, такие как доступ к базе данных, фактически "параллельны" тем, что сами запросы к базе данных выполняются в отдельных потоках, но при завершении их реинтегрируются в поток событий.
Итак, как вы планируете обратный вызов для нескольких обработчиков событий? Ну, это один из распространенных методов, используемых в анимации в браузере javascript: используйте переменную для отслеживания завершения.
Это звучит как хак, и это так, и это звучит потенциально беспорядочно, оставляя кучу глобальных переменных вокруг отслеживания и на меньшем языке. Но в 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 не требуют аргументов. Разумеется, вы можете модифицировать код для передачи аргументов в функции async, а функция обратного вызова накапливает результаты и передает их функции 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 или различные библиотеки, основанные на обещаниях. Я хотел бы поверить, что async.js был вдохновлен этим, но у меня нет никаких доказательств этого. В любом случае.. если вы подумываете об этом сегодня, взгляните на async.js или обещания. Просто рассмотрите ответ выше хорошего объяснения/иллюстрации того, как такие вещи, как async.parallel work.
Для полноты async.parallel
следующим образом: как вы это сделаете с помощью async.parallel
:
var async = require('async');
async.parallel([A,B,C],D);
Обратите внимание, что async.parallel
работает точно так же, как и функция fork
реализованная выше. Основное различие заключается в том, что он передает ошибку в качестве первого аргумента D
и обратного вызова в качестве второго аргумента в соответствии с соглашением node.js.
Используя обещания, мы напишем его следующим образом:
// Assuming A, B & C return a promise instead of accepting a callback
Promise.all([A,B,C]).then(D);