method - (JavaScript) Sincronizando forEach Loop con devoluciones de llamada dentro
javascript for each array in array (5)
Estoy haciendo algunos cálculos dentro de un doble para forEach
Loop algo como esto:
array.forEach(function(element){
Object.keys(element).forEach(function(key){
/* some complex computations with asynchronous callbacks */
});
});
someFunctionHere();
¿Hay alguna forma de que el Loop termine primero antes de realizar la función someFunctionHere( )
? o de cualquier forma que el programa sabrá si el bucle se termina antes de continuar a someFunctionHere( )
...
Puede que me falten algunos foros, pero los que encontré no me ayudaron con lo que quiero lograr y, por la forma en que lo hago en NodeJS, también estoy preguntando si hay bibliotecas existentes que puedan hacer que esto suceda.
Olvidé agregar esto, ¿o debería ser otra pregunta?
¿Hay una manera de hacer la iteración de forma síncrona que solo procederá a la siguiente iteración una vez que se realiza la iteración actual? (Perdón por esto)
Gracias por cualquier ayuda...
Eche un vistazo a async.js, y especialmente a sus estados de flujo de control , como each
whilst
y until
.
Usando async.js puedes obtener lo que quieres tener.
En su situación real, lo que desea es each
función (que antes se conocía como forEach
), respectivamente la función eachSeries
que no ejecuta las iteraciones individuales en paralelo, sino en serie (consulte la documentación de cada Serie para obtener más detalles).
Para dar un ejemplo:
async.eachSeries([ 2, 3, 5, 7, 11 ], function (prime, callback) {
console.log(prime);
callback(); // Alternatively: callback(new Error());
}, function (err) {
if (err) { throw err; }
console.log(''Well done :-)!'');
});
Esto recorrerá la matriz de números primos y los imprimirá en el orden correcto, uno tras otro, y finalmente se imprimirá ¡ Well done :-)!
.
Para los navegadores que son compatibles con Promise (o con polyfill) / nodejs, he implementado una versión de sincronización de forEach con devolución de llamada, así que solo la compartiré aquí ...
La devolución de llamada debe devolver una promesa ..
function forEachSync(array, callback) {
let lastIndex = array.length - 1;
let startIndex = 0;
return new Promise((resolve, reject) => { // Finish all
let functionToIterateWith = (currIndex) => {
if (currIndex > lastIndex) {
return resolve();
} else {
callback(array[currIndex]).then(() => {
functionToIterateWith(currIndex + 1);
}).catch(err => reject(err));
}
}
functionToIterateWith(startIndex);
});
}
Puede ajustar sus devoluciones de llamada en un cierre de cuenta regresiva:
var len = array.length;
function countdownWrapper(callback, callbackArgs) {
callback(callbackArgs);
if (--len == 0) {
someFunctionHere();
}
}
array.forEach(function(element){
Object.keys(element).forEach(function(key){
var wrappedCallback = countdownWrapper.bind(callback);
/* some complex computations with asynchronous WRAPPED callbacks */
});
});
Si las callbackArgs
tienen un número diferente de argumentos, puede realizar una pequeña cirugía en los arguments
lugar de usar un parámetro de callbackArgs
explícito.
EDITAR Su edición aclara que desea iniciar cada cálculo complejo después de que el cálculo anterior complete su devolución de llamada. Esto también se puede arreglar fácilmente a través de cierres:
function complexOp(key, callback) { /* . . . */ }
function originalCallback(...) { /* . . . */ }
function doSomethingElse() { /* . . . */ }
var iteratorCallback = (function (body, callback, completion) {
var i = 0, len = array.length;
return function iterator() {
callback.apply(null, arguments);
if (++i < len) {
body(array[i], iterator);
} else {
completion();
}
};
}(complexOp, originalCallback, doSomethingElse));
// kick everything off:
complexOp(array[0], iteratorCallback);
SOLUCIÓN DE ALTO RENDIMIENTO:
Puede haber un caso en el que desee / permita procesar la matriz de forma asincrónica / paralela pero desea que se llame a una función después de que se hayan procesado todos los miembros de forEach. Ejemplo:
var async = require(''async'');
async.forEach(array,function(elementOfArray, callback){
//process each element of array in parallel
// ..
// ..
// ..
callback(); //notify that this iteration is complete
}, function(err){
if(err){throw err;}
console.log("processing all elements completed");
});
Por lo tanto, de esta manera usted realiza operaciones intensivas de CPU sin bloqueo.
NOTA: Cuando usa cada serie para arreglos enormes, muchas devoluciones de llamada iterativas pueden desbordar la pila.
let dataObject = {};
Promise.all(objectArray.map(object => {
return new Promise(resolve => {
yourFunction(object, anotherParameter).then(returnValue => {
dataObject[object] = returnValue;
resolve();
});
});
})).then(() => {
return dataObject;
});