У 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);
}
}
fork([A,B,C],D);
У наведеному вище прикладі ми робимо код простим, припускаючи, що функції асинхронізації та зворотного виклику не вимагають аргументів. Звичайно, ви можете змінити код, щоб передавати аргументи асинхронним функціям, і функція зворотного виклику накопичуватиме результати та передаватиме їх у функцію 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 = [];
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:
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);
Оновлення
Цей код був написаний до існування таких бібліотек, як async.js або різноманітних бібліотек, заснованих на обіцянках. Я хотів би вірити, що async.js був натхненний цим, але я не маю жодних доказів цього. У будь-якому випадку .. якщо ви думаєте зробити це сьогодні, подивіться на async.js або обіцянки. Просто розгляньте відповідь вище на гарне пояснення / ілюстрацію того, як працюють такі речі, як async.parallel.
Для повноти наступне, як ви це зробите async.parallel
:
var async = require('async');
async.parallel([A,B,C],D);
Зверніть увагу, що async.parallel
працює точно так само, як fork
функція, яку ми реалізували вище. Головна відмінність полягає в тому, що він передає помилку як перший аргумент, D
а зворотний виклик - як другий аргумент згідно з умовою node.js.
Використовуючи обіцянки, ми писали б це так:
Promise.all([A,B,C]).then(D);