Я хотел бы предложить другое решение, которое использует скорость и эффективность парадигмы программирования в самом ядре событий Node :.
Все, что вы можете делать с промисами или модулями, предназначенными для управления потоком, например async, может быть выполнено с помощью событий и простого конечного автомата, который, я считаю, предлагает методологию, которая, возможно, легче понять, чем другие варианты.
Например, предположим, что вы хотите суммировать длину нескольких файлов параллельно:
const EventEmitter = require('events').EventEmitter;
// simple event-driven state machine
const sm = new EventEmitter();
// running state
let context={
tasks: 0, // number of total tasks
active: 0, // number of active tasks
results: [] // task results
};
const next = (result) => { // must be called when each task chain completes
if(result) { // preserve result of task chain
context.results.push(result);
}
// decrement the number of running tasks
context.active -= 1;
// when all tasks complete, trigger done state
if(!context.active) {
sm.emit('done');
}
};
// operational states
// start state - initializes context
sm.on('start', (paths) => {
const len=paths.length;
console.log(`start: beginning processing of ${len} paths`);
context.tasks = len; // total number of tasks
context.active = len; // number of active tasks
sm.emit('forEachPath', paths); // go to next state
});
// start processing of each path
sm.on('forEachPath', (paths)=>{
console.log(`forEachPath: starting ${paths.length} process chains`);
paths.forEach((path) => sm.emit('readPath', path));
});
// read contents from path
sm.on('readPath', (path) => {
console.log(` readPath: ${path}`);
fs.readFile(path,(err,buf) => {
if(err) {
sm.emit('error',err);
return;
}
sm.emit('processContent', buf.toString(), path);
});
});
// compute length of path contents
sm.on('processContent', (str, path) => {
console.log(` processContent: ${path}`);
next(str.length);
});
// when processing is complete
sm.on('done', () => {
const total = context.results.reduce((sum,n) => sum + n);
console.log(`The total of ${context.tasks} files is ${total}`);
});
// error state
sm.on('error', (err) => { throw err; });
// ======================================================
// start processing - ok, let's go
// ======================================================
sm.emit('start', ['file1','file2','file3','file4']);
Что выведет:
start: начало обработки 4-х дорожек
forEachPath: запуск 4 цепочек процессов
readPath: file1
readPath: file2
processContent: file1
readPath: file3
processContent: file2
processContent: file3
readPath: file4
processContent: file4
Всего 4 файла 4021
Обратите внимание, что порядок задач цепочки процессов зависит от загрузки системы.
Вы можете представить себе выполнение программы как:
начало -> forEachPath - + -> readPath 1 -> processContent 1 - + -> готово
+ -> readFile 2 -> processContent 2 - +
+ -> readFile 3 -> processContent 3 - +
+ -> readFile 4 -> processContent 4 - +
Для повторного использования было бы тривиально создать модуль для поддержки различных шаблонов управления потоком, то есть последовательного, параллельного, пакетного, пока, пока и т. Д.