Wie der Titel schon sagt. Wie mache ich das?
Ich möchte whenAllDone()
aufrufen, nachdem die forEach-Schleife jedes Element durchlaufen hat und eine asynchrone Verarbeitung ausgeführt hat.
[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();
}
);
Möglich, dass es so funktioniert? Wenn das zweite Argument für forEach eine Rückruffunktion ist, die ausgeführt wird, wenn alle Iterationen durchlaufen wurden?
Erwartete Ausgabe:
3 done
1 done
2 done
All done!
Array.forEach
bietet diese Sicherheit nicht (oh wenn es so wäre), aber es gibt mehrere Möglichkeiten, um das zu erreichen, was Sie wollen:
function callback () { console.log('all done'); }
var itemsProcessed = 0;
[1, 2, 3].forEach((item, index, array) => {
asyncFunction(item, () => {
itemsProcessed++;
if(itemsProcessed === array.length) {
callback();
}
});
});
(Dank an @vanuan und andere) Dieser Ansatz garantiert, dass alle Elemente verarbeitet werden, bevor der Rückruf "done" aufgerufen wird. Sie müssen einen Zähler verwenden, der im Callback aktualisiert wird. Abhängig vom Wert des Index-Parameters bietet sich nicht die gleiche Garantie, da die Reihenfolge der Rückgabe der asynchronen Vorgänge nicht garantiert wird.
(Eine Versprechenbibliothek kann für ältere Browser verwendet werden):
Verarbeiten Sie alle Anforderungen, die eine synchrone Ausführung gewährleisten (z. B. 1 dann 2 dann 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'))
Verarbeiten Sie alle asynchronen Anforderungen ohne "synchrone" Ausführung (2 können schneller abgeschlossen werden als 1)
let requests = [1,2,3].map((item) => {
return new Promise((resolve) => {
asyncFunction(item, resolve);
});
})
Promise.all(requests).then(() => console.log('done'));
Es gibt andere asynchrone Bibliotheken, wobei async die populärste ist, die Mechanismen zur Verfügung stellen, um auszudrücken, was Sie wollen.
Der Hauptteil der Frage wurde bearbeitet, um den zuvor synchronen Beispielcode zu entfernen. Daher habe ich meine Antwort aktualisiert, um klarer zu stellen .. Das ursprüngliche Beispiel verwendete synchronen Code, um asynchrones Verhalten zu modellieren. Daher wurde Folgendes angewendet:
array.forEach
ist synchronous und so ist res.write
, so dass Sie Ihren Rückruf nach Ihrem Anruf bei foreach einfach setzen können:
posts.foreach(function(v, i) {
res.write(v + ". index " + i);
});
res.end();
wenn Sie auf asynchrone Funktionen stoßen und sicherstellen möchten, dass der Task vor Ausführung des Codes ausgeführt wird, können Sie immer die Rückruffunktion verwenden.
zum Beispiel:
var ctr = 0;
posts.forEach(function(element, index, array){
asynchronous(function(data){
ctr++;
if (ctr === array.length) {
functionAfterForEach();
}
})
});
anmerkung: functionAfterForEach ist eine Funktion, die ausgeführt wird, nachdem foreach-Aufgaben abgeschlossen sind . asynchron ist die asynchrone Funktion, die in foreach ausgeführt wird.
hoffe das hilft.
Es ist merkwürdig, wie viele falsche Antworten auf den asynchronous -Fall! .__ gegeben wurden. Es kann einfach gezeigt werden, dass das Überprüfen des Index kein erwartetes Verhalten liefert:
// INCORRECT
var list = [4000, 2000];
list.forEach(function(l, index) {
console.log(l + ' started ...');
setTimeout(function() {
console.log(index + ': ' + l);
}, l);
});
ausgabe:
4000 started
2000 started
1: 2000
0: 4000
Wenn wir nach index === array.length - 1
suchen, wird der Callback nach Abschluss der ersten Iteration aufgerufen, während das erste Element noch aussteht!
Um dieses Problem zu lösen, ohne externe Bibliotheken wie async zu verwenden, denke ich, ist es am besten, wenn Sie nach jeder Iteration Länge der Liste und Dekrementierung sparen. Da es nur einen Thread gibt, sind wir uns sicher, dass es keine Chance auf Rennen gibt.
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);
});
Ich hoffe, das wird Ihr Problem beheben. Normalerweise arbeite ich damit, wenn ich forEach mit asynchronen Aufgaben ausführen muss.
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
}
}
mit
function doAsynchronousFunction(entry,callback){
//asynchronousjob with entry
callback();
}
Meine Lösung ohne Versprechen (dadurch wird sichergestellt, dass jede Aktion beendet wird, bevor die nächste beginnt):
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>
var counter = 0;
var listArray = [0, 1, 2, 3, 4];
function callBack() {
if (listArray.length === counter) {
console.log('All Done')
}
};
listArray.forEach(function(element){
console.log(element);
counter = counter + 1;
callBack();
});
Mit ES2018 können Sie asynchrone Iteratoren verwenden:
const asyncFunction = a => fetch(a);
const itemDone = a => console.log(a);
async function example() {
const arrayOfFetchPromises = [1, 2, 3].map(asyncFunction);
for await (const item of arrayOfFetchPromises) {
itemDone(item);
}
console.log('All done');
}
Dies ist die Lösung für Node.js, die asynchron ist.
verwenden des asynchronen npm-Pakets.
(JavaScript) forEach Loop mit Callbacks im Inneren synchronisieren
Ich versuche es mit Easy Way zu lösen, teile es mit dir:
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
ist Funktion der mssql-Bibliothek in Knoten js. Dies kann jede Funktion oder jeden Code ersetzen, den Sie wollen . GoodLuck
Meine Lösung:
//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();
}
});
}
});
Beispiel:
var arr = ['a', 'b', 'c'];
arr.forEachDone(function(item){
console.log(item);
}, function(){
console.log('done');
});
// out: a b c done
var i=0;
const waitFor = (ms) =>
{
new Promise((r) =>
{
setTimeout(function () {
console.log('timeout completed: ',ms,' : ',i);
i++;
if(i==data.length){
console.log('Done')
}
}, ms);
})
}
var data=[1000, 200, 500];
data.forEach((num) => {
waitFor(num)
})