javascript - pagina - js promesas anidadas
Devolución de llamada después de todo asíncrono para cada devolución de llamada se completan (11)
¡Es extraño cuántas respuestas incorrectas se han dado al caso asincrónico ! Se puede demostrar simplemente que el índice de verificación no proporciona el comportamiento esperado:
// INCORRECT
var list = [4000, 2000];
list.forEach(function(l, index) {
console.log(l + '' started ...'');
setTimeout(function() {
console.log(index + '': '' + l);
}, l);
});
salida:
4000 started
2000 started
1: 2000
0: 4000
Si buscamos el index === array.length - 1
, se llamará a la devolución de llamada una vez completada la primera iteración, ¡mientras que el primer elemento aún está pendiente!
Para resolver este problema sin utilizar bibliotecas externas como async, creo que la mejor opción es guardar la longitud de la lista y disminuirla después de cada iteración. Como solo hay un hilo, estamos seguros de que no hay posibilidad de una condición de carrera.
var list = [4000, 2000];
var counter = list.length;
list.forEach(function(l, index) {
console.log(l + '' started ...'');
setTimeout(function() {
console.log(index + '': '' + l);
counter -= 1;
if ( counter === 0)
// call your callback here
}, l);
});
Como sugiere el título. ¿Cómo hago esto?
Quiero llamar a whenAllDone()
después de que forEach-loop haya revisado cada elemento y haya realizado un procesamiento asincrónico.
[1, 2, 3].forEach(
function(item, index, array, done) {
asyncFunction(item, function itemDone() {
console.log(item + " done");
done();
});
}, function allDone() {
console.log("All done");
whenAllDone();
}
);
¿Es posible hacer que funcione así? ¿Cuando el segundo argumento para forEach es una función de devolución de llamada que se ejecuta una vez que pasó por todas las iteraciones?
Rendimiento esperado:
3 done
1 done
2 done
All done!
¿Qué tal setInterval, para verificar el conteo de iteración completa, trae garantía. Sin embargo, no estoy seguro si no sobrecargará el alcance pero lo uso y parece ser el
_.forEach(actual_JSON, function (key, value) {
// run any action and push with each iteration
array.push(response.id)
});
setInterval(function(){
if(array.length > 300) {
callback()
}
}, 100);
Espero que esto solucione tu problema, generalmente trabajo con esto cuando necesito ejecutar para cada uno con tareas asincrónicas dentro.
foo = [a,b,c,d];
waiting = foo.length;
foo.forEach(function(entry){
doAsynchronousFunction(entry,finish) //call finish after each entry
}
function finish(){
waiting--;
if (waiting==0) {
//do your Job intended to be done after forEach is completed
}
}
con
function doAsynchronousFunction(entry,callback){
//asynchronousjob with entry
callback();
}
Esta es la solución para Node.js que es asincrónico.
usando el paquete async npm.
(JavaScript) Sincronización para cada bucle con devoluciones de llamada dentro
Intento Easy Way para resolverlo, compartirlo contigo:
let counter = 0;
arr.forEach(async (item, index) => {
await request.query(item, (err, recordset) => {
if (err) console.log(err);
//do Somthings
counter++;
if(counter == tableCmd.length){
sql.close();
callback();
}
});
request
es Función de la biblioteca mssql en el nodo js. Esto puede reemplazar cada función o Código que desee. Buena suerte
Mi solución sin Promesa (esto garantiza que cada acción finalice antes de que comience la próxima):
Array.prototype.forEachAsync = function (callback, end) {
var self = this;
function task(index) {
var x = self[index];
if (index >= self.length) {
end()
}
else {
callback(self[index], index, self, function () {
task(index + 1);
});
}
}
task(0);
};
var i = 0;
var myArray = Array.apply(null, Array(10)).map(function(item) { return i++; });
console.log(JSON.stringify(myArray));
myArray.forEachAsync(function(item, index, arr, next){
setTimeout(function(){
$(".toto").append("<div>item index " + item + " done</div>");
console.log("action " + item + " done");
next();
}, 300);
}, function(){
$(".toto").append("<div>ALL ACTIONS ARE DONE</div>");
console.log("ALL ACTIONS ARE DONE");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="toto">
</div>
Mi solución:
//Object forEachDone
Object.defineProperty(Array.prototype, "forEachDone", {
enumerable: false,
value: function(task, cb){
var counter = 0;
this.forEach(function(item, index, array){
task(item, index, array);
if(array.length === ++counter){
if(cb) cb();
}
});
}
});
//Array forEachDone
Object.defineProperty(Object.prototype, "forEachDone", {
enumerable: false,
value: function(task, cb){
var obj = this;
var counter = 0;
Object.keys(obj).forEach(function(key, index, array){
task(obj[key], key, obj);
if(array.length === ++counter){
if(cb) cb();
}
});
}
});
Ejemplo:
var arr = [''a'', ''b'', ''c''];
arr.forEachDone(function(item){
console.log(item);
}, function(){
console.log(''done'');
});
// out: a b c done
No debería necesitar una devolución de llamada para iterar a través de una lista. Simplemente agregue la llamada al end()
después del ciclo.
posts.forEach(function(v, i){
res.write(v + ". Index " + i);
});
res.end();
Una solución simple sería como seguir
function callback(){console.log("i am done");}
["a", "b", "c"].forEach(function(item, index, array){
//code here
if(i == array.length -1)
callback()
}
si encuentra funciones asíncronas, y quiere asegurarse de que antes de ejecutar el código finalice su tarea, siempre podemos usar la función de devolución de llamada.
por ejemplo:
var ctr = 0;
posts.forEach(function(element, index, array){
asynchronous(function(data){
ctr++;
if (ctr === array.length) {
functionAfterForEach();
}
})
});
nota: functionAfterForEach es la función que se ejecutará después de que las tareas de foreach hayan finalizado. asincrónico es la función asíncrona ejecutada dentro de foreach.
espero que esto ayude.
Array.forEach
no proporciona esta calidad (oh, si lo fuera), pero hay varias formas de lograr lo que desea:
Usando un simple contador
function callback () { console.log(''all done''); }
var itemsProcessed = 0;
[1, 2, 3].forEach((item, index, array) => {
asyncFunction(item, () => {
itemsProcessed++;
if(itemsProcessed === array.length) {
callback();
}
});
});
(gracias a @vanuan y otros) Este enfoque garantiza que todos los elementos se procesen antes de invocar la devolución de llamada "realizada". El enfoque que sugiere Emil, aunque generalmente es efectivo en mi experiencia, no ofrece la misma garantía.
Usando ES6 Promises
(una biblioteca de promesas se puede usar para navegadores antiguos):
Procese todas las solicitudes que garanticen la ejecución sincrónica (p. Ej., 1, luego 2 y 3)
function asyncFunction (item, cb) { setTimeout(() => { console.log(''done with'', item); cb(); }, 100); } let requests = [1, 2, 3].reduce((promiseChain, item) => { return promiseChain.then(() => new Promise((resolve) => { asyncFunction(item, resolve); })); }, Promise.resolve()); requests.then(() => console.log(''done''))
Procese todas las solicitudes asincrónicas sin ejecución "síncrona" (2 pueden finalizar más rápido que 1)
let requests = [1,2,3].map((item) => { return new Promise((resolve) => { asyncFunction(item, resolve); }); }) Promise.all(requests).then(() => console.log(''done''));
Usando una biblioteca asíncrona
Existen otras bibliotecas asíncronas, siendo async la más popular, que proporcionan mecanismos para expresar lo que desea.
EditarEl cuerpo de la pregunta ha sido editado para eliminar el código de ejemplo previamente sincrónico, así que actualicé mi respuesta para aclarar. El ejemplo original utilizaba código sincrónico similar para modelar el comportamiento asincrónico, por lo que se aplica lo siguiente:
array.forEach
es synchronous y también lo es res.write
, por lo que simplemente puede poner su devolución de llamada después de su llamada a foreach:
posts.foreach(function(v, i) {
res.write(v + ". index " + i);
});
res.end();