Reputation: 16204
As the title suggests. How do I do this?
I want to call whenAllDone()
after the forEach-loop has gone through each element and done some asynchronous processing.
[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();
}
);
Possible to get it to work like this? When the second argument to forEach is a callback function which runs once it went through all iterations?
Expected output:
3 done
1 done
2 done
All done!
Upvotes: 301
Views: 419393
Reputation: 2709
There are many solutions and ways to achieve this on this thread!.
But, if you need do this with map and async/await then here it is
// Execution Starts
console.log("start")
// The Map will return promises
// the Execution will not go forward until all the promises are resolved.
await Promise.all(
[1, 2, 3].map( async (item) => {
await asyncFunction(item)
})
)
// Will only run after all the items have resolved the asynchronous function.
console.log("End")
The output will be something like this! May vary based on the asynchronous function.
start
2
3
1
end
Note: If you use await in a map, it will always return promise.
Upvotes: 3
Reputation: 1
My Solution
const items=[1, 2, 3]
items.forEach(async (item) => {
console.log(item + " done");
if(items.indexOf(item) == items.length-1){
console.log("All done");
}
})
Upvotes: -3
Reputation: 62
You can now call your function with the async
declaration and await
keyword to pause execution inside your function until a statement is resolved.
By labeling your containing function call with async
you're able to prevent lines of code below your await
statement from running until it has resolved. Note, if your await statement fails your function will not continue to execute.
To use the original code example:
async wrapperFunction() {
await [1, 2, 3].forEach(item => {
console.log(item + " done"); // Iteratively process individual item in array
});
console.log("All done"); // Executes once the awaited forEach() completes
};
wrapperFunction();
Console output:
1 done
2 done
3 done
All done
Upvotes: -2
Reputation: 2961
If you encounter asynchronous functions, and you want to make sure that before executing the code it finishes its task, we can always use the callback capability.
For example:
var ctr = 0;
posts.forEach(function(element, index, array){
asynchronous(function(data){
ctr++;
if (ctr === array.length) {
functionAfterForEach();
}
})
});
Note: functionAfterForEach
is the function to be executed after foreach tasks are finished. asynchronous
is the asynchronous function executed inside foreach.
Upvotes: 35
Reputation: 410
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();
});
Upvotes: -1
Reputation: 29211
Array.forEach
does not provide this nicety (oh if it would) but there are several ways to accomplish what you want:
function callback () { console.log('all done'); }
var itemsProcessed = 0;
[1, 2, 3].forEach((item, index, array) => {
asyncFunction(item, () => {
itemsProcessed++;
if(itemsProcessed === array.length) {
callback();
}
});
});
(thanks to @vanuan and others) This approach guarantees that all items are processed before invoking the "done" callback. You need to use a counter that gets updated in the callback. Depending on the value of the index parameter does not provide the same guarantee, because the order of return of the asynchronous operations is not guaranteed.
(a promise library can be used for older browsers):
Process all requests guaranteeing synchronous execution (e.g. 1 then 2 then 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'))
Process all async requests without "synchronous" execution (2 may finish faster than 1)
let requests = [1,2,3].map((item) => {
return new Promise((resolve) => {
asyncFunction(item, resolve);
});
})
Promise.all(requests).then(() => console.log('done'));
There are other asynchronous libraries, async being the most popular, that provide mechanisms to express what you want.
EditThe body of the question has been edited to remove the previously synchronous example code, so i've updated my answer to clarify. The original example used synchronous like code to model asynchronous behaviour, so the following applied:
array.forEach
is synchronous and so is res.write
, so you can simply put your callback after your call to foreach:
posts.foreach(function(v, i) {
res.write(v + ". index " + i);
});
res.end();
Upvotes: 520
Reputation: 9406
With ES2018 you can use async iterators:
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');
}
Upvotes: 11
Reputation: 683
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)
})
Upvotes: -1
Reputation: 300
I try Easy Way to resolve it, share it with you :
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
is Function of mssql Library in Node js. This can replace each function or Code u want.
GoodLuck
Upvotes: -1
Reputation: 313
My solution without Promise (this ensures that every action is ended before the next one begins):
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>
Upvotes: 3
Reputation: 7742
It's odd how many incorrect answers has been given to asynchronous case! It can be simply shown that checking index does not provide expected behavior:
// INCORRECT
var list = [4000, 2000];
list.forEach(function(l, index) {
console.log(l + ' started ...');
setTimeout(function() {
console.log(index + ': ' + l);
}, l);
});
output:
4000 started
2000 started
1: 2000
0: 4000
If we check for index === array.length - 1
, callback will be called upon completion of first iteration, whilst first element is still pending!
To solve this problem without using external libraries such as async, I think your best bet is to save length of list and decrement if after each iteration. Since there's just one thread we're sure there no chance of race condition.
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);
});
Upvotes: 18
Reputation: 201
Hope this will fix your problem, i usually work with this when i need to execute forEach with asynchronous tasks inside.
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
}
}
with
function doAsynchronousFunction(entry,callback){
//asynchronousjob with entry
callback();
}
Upvotes: 19
Reputation: 5930
You shouldn't need a callback for iterating through a list. Just add the end()
call after the loop.
posts.forEach(function(v, i){
res.write(v + ". Index " + i);
});
res.end();
Upvotes: -5