Callback po zakończeniu wszystkich asynchronicznych połączeń zwrotnych forEach
Jak sugeruje tytuł. Jak to zrobić?
Chcę wywołać whenAllDone()
po tym, jak pętla forEach przejdzie przez każdy element i zrobi asynchroniczne przetwarzanie.
[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();
}
);
Możliwe, aby to działało w ten sposób? Kiedy drugim argumentem forEach jest funkcja callback, która działa po przejściu przez wszystkie iteracje?
Oczekiwany wynik:
3 done
1 done
2 done
All done!
13 answers
Array.forEach
nie zapewnia tego nicości (o, gdyby tak było), ale jest kilka sposobów, aby osiągnąć to, co chcesz:
Używanie prostego licznika
function callback () { console.log('all done'); }
var itemsProcessed = 0;
[1, 2, 3].forEach((item, index, array) => {
asyncFunction(item, () => {
itemsProcessed++;
if(itemsProcessed === array.length) {
callback();
}
});
});
(dzięki @vanuan i innym) to podejście gwarantuje, że wszystkie elementy są przetwarzane przed wywołaniem wywołania zwrotnego "gotowe". Podejście sugerowane przez Emila, choć z mojego doświadczenia typowo skuteczne, nie daje takiej samej gwarancji.
Używanie obietnic ES6
(biblioteka promise może być używana dla starszych przeglądarki):
-
Przetwarza wszystkie żądania gwarantujące synchroniczne wykonanie (np. 1, 2, 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'))
-
Przetwarza wszystkie żądania asynchroniczne bez" synchronicznego " wykonania (2 może zakończyć się szybciej niż 1)
let requests = [1,2,3].map((item) => { return new Promise((resolve) => { asyncFunction(item, resolve); }); }) Promise.all(requests).then(() => console.log('done'));
Korzystanie z biblioteki asynchronicznej
Istnieją inne biblioteki asynchroniczne, async są najbardziej popularne, które zapewniają mechanizmy wyrażania tego, co chcesz.
EdytujTreść Pytania została zredagowana aby usunąć wcześniej synchroniczny przykładowy kod, więc zaktualizowałem moją odpowiedź, aby wyjaśnić. Oryginalny przykład używał kodu synchronicznego do modelowania asynchronicznego zachowania, więc zastosowano następujące:
array.forEach
jest synchroniczne i tak jest res.write
, więc możesz po prostu umieścić swoje callback po połączeniu do foreach:
posts.foreach(function(v, i) {
res.write(v + ". index " + i);
});
res.end();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:21
Jeśli napotkasz funkcje asynchroniczne i chcesz się upewnić, że przed wykonaniem kodu zakończy swoje zadanie, zawsze możemy użyć funkcji callback.
Na przykład:
var ctr = 0;
posts.forEach(function(element, index, array){
asynchronous(function(data){
ctr++;
if (ctr === array.length) {
functionAfterForEach();
}
})
});
Uwaga: functionAfterForEach jest funkcją wykonywaną po zakończeniu zadań foreach. asynchroniczna jest funkcją asynchroniczną wykonywaną wewnątrz foreach.
Mam nadzieję, że to pomoże.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-10 01:49:27
To dziwne, ile błędnych odpowiedzi zostało udzielonych asynchronicznej przypadku! Można po prostu pokazać, że sprawdzanie indeksu nie zapewnia oczekiwanego zachowania:
// INCORRECT
var list = [4000, 2000];
list.forEach(function(l, index) {
console.log(l + ' started ...');
setTimeout(function() {
console.log(index + ': ' + l);
}, l);
});
Wyjście:
4000 started
2000 started
1: 2000
0: 4000
Jeśli sprawdzimy index === array.length - 1
, wywołanie callback zostanie wywołane po zakończeniu pierwszej iteracji, podczas gdy pierwszy element jest jeszcze w toku!
Aby rozwiązać ten problem bez korzystania z zewnętrznych bibliotek, takich jak async, myślę, że najlepiej jest zapisać długość listy i zmniejszyć, jeśli po każdym iteracja. Ponieważ jest tylko jeden wątek, jesteśmy pewni, że nie ma szans na stan rasy.
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);
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-15 10:54:56
Mam nadzieję, że to rozwiąże twój problem, zwykle pracuję z tym, gdy muszę wykonać forEach z asynchronicznymi zadaniami wewnątrz.
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
}
}
Z
function doAsynchronousFunction(entry,callback){
//asynchronousjob with entry
callback();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-12 14:31:14
Moje rozwiązanie bez obietnicy (zapewnia to, że każda akcja kończy się przed rozpoczęciem następnej):
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>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-26 21:19:58
Z ES2018 możesz używać iteratorów asynchronicznych:
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');
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-15 10:29:32
Jest To rozwiązanie dla Node.js, który jest asynchroniczny.
Używanie pakietu async npm.
(JavaScript) Synchronizacja pętli forEach z wywołaniami zwrotnymi wewnątrz
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:29
Jak o setInterval, aby sprawdzić pełną liczbę iteracji, przynosi gwarancję. Nie wiem, czy nie przeciąży lunety, ale używam jej i wydaje się być tą
_.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);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-25 00:42:19
Moje rozwiązanie:
//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();
}
});
}
});
Przykład:
var arr = ['a', 'b', 'c'];
arr.forEachDone(function(item){
console.log(item);
}, function(){
console.log('done');
});
// out: a b c done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-16 09:50:54
Staram się łatwo to rozwiązać, podzielić się z Tobą:
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
jest funkcją biblioteki mssql w Node js. Może to zastąpić każdą funkcję lub kod, który chcesz.
GoodLuck
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-09 06:11:38
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)
})
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-16 05:12:51
Proste rozwiązanie byłoby jak follow
function callback(){console.log("i am done");}
["a", "b", "c"].forEach(function(item, index, array){
//code here
if(i == array.length -1)
callback()
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-22 14:36:40
Nie powinieneś potrzebować połączenia zwrotnego do iteracji listy. Wystarczy dodać wywołanie end()
po pętli.
posts.forEach(function(v, i){
res.write(v + ". Index " + i);
});
res.end();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-24 13:38:32