Me gustaría ofrecer otra solución que utiliza la velocidad y la eficiencia del paradigma de programación en el núcleo mismo de Node: events.
Todo lo que puede hacer con Promesas o módulos diseñados para administrar el control de flujo, como async, se puede lograr usando eventos y una máquina de estado simple, que creo que ofrece una metodología que es, quizás, más fácil de entender que otras opciones.
Por ejemplo, suponga que desea sumar la longitud de varios archivos en paralelo:
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']);
Que dará como resultado:
inicio: inicio del procesamiento de 4 rutas
forEachPath: iniciando 4 cadenas de procesos
readPath: file1
readPath: file2
processContent: file1
readPath: file3
processContent: file2
processContent: file3
readPath: file4
processContent: file4
El total de 4 archivos es 4021
Tenga en cuenta que el orden de las tareas de la cadena de procesos depende de la carga del sistema.
Puede visualizar el flujo del programa como:
inicio -> forEachPath - + -> readPath 1 -> processContent 1 - + -> hecho
+ -> readFile 2 -> processContent 2 - +
+ -> readFile 3 -> processContent 3 - +
+ -> readFile 4 -> processContent 4 - +
Para su reutilización, sería trivial crear un módulo que admita los diversos patrones de control de flujo, es decir, serie, paralelo, lote, mientras, hasta, etc.