I want to run a code after my async forEach loop.
myPosts.forEach(function(post) {
getPostAuthor(post.authorID, function(postAuthor) {
post.author = postAuthor;
}
});
res.render('index', {
posts: myPosts
});
res.end();
in code above first res.render runs and after that forEach fills the post.author
I want to run a code after my async forEach loop.
myPosts.forEach(function(post) {
getPostAuthor(post.authorID, function(postAuthor) {
post.author = postAuthor;
}
});
res.render('index', {
posts: myPosts
});
res.end();
in code above first res.render runs and after that forEach fills the post.author
Share Improve this question edited Aug 8, 2017 at 14:55 user5182503 asked Aug 8, 2017 at 14:52 newer studentnewer student 951 gold badge3 silver badges10 bronze badges 4- forEach is not async. getPostAuthor is. – Helder De Baere Commented Aug 8, 2017 at 14:56
-
No, but I bet
getPostAuthor()
is. – Jordan Kasper Commented Aug 8, 2017 at 14:56 - i mean function in forEach – newer student Commented Aug 8, 2017 at 14:57
- There are hundreds of duplicates of this here on stack overflow. – jfriend00 Commented Aug 8, 2017 at 15:47
6 Answers
Reset to default 6Rather map to Promises than iterating with forEach, then use Promise.all:
Promise.all(
myPosts.map(function(post) {
return new Promise(function(res){
getPostAuthor(post.authorID, function(postAuthor) {
post.author = postAuthor;
res(postAuthor);
});
});
})
).then(function(authors){
res.render('index', {
posts: myPosts
});
res.end();
});
You can create an array of promises, then listen for all pletions using Promise.all
.
const promises = [];
myPosts.forEach(function(post) {
const promise = new Promise((resolve) => {
getPostAuthor(post.authorID, function(postAuthor) {
post.author = postAuthor;
resolve(); // plete the current promise
}
});
promises.push(promise);
});
Promise.all(promises).then(() => {
res.render('index', {
posts: myPosts
});
res.end();
});
You could do this 2 ways, you could make/use a Promise or use a counting method.
Counting method:
var numComplete = 0;
var done = function() {
if(numComplete >= myPosts.length) {
// Code to run once finished
}
};
myPosts.forEach(function(post) {
getPostAuthor(post.authorID, function(postAuthor) {
post.author = postAuthor;
numComplete++;
done();
}
});
You could use a third-party lib like Async.
Example:
import each from 'async/each';
var elements = [1, 2, 3, 4, 5, 6];
each(elements, function(el, next) {
console.log('Processing element ' + el);
callAsyncFunction(next); //your async function should accept a callback
}, function(err){
//this is your ending function. It'll be called after all elements have been processed
if( err ) {
console.log('A file failed to process');
} else {
console.log('Async processing is finished.');
}
});
Your code should look like:
each(myPosts, function(post, next) {
getPostAuthor(post.authorID, function(postAuthor) {
post.author = postAuthor;
next()
});
}, function(err) {
if (!err) {
res.render('index', {
posts: myPosts
});
res.end();
}
});
You could try async/await
with for loop
.
const sleep = timeout => new Promise(resolve => setTimeout(() => resolve(), timeout));
const myPosts = [1,2,3];
const fetchPosts = async () => {
for (let post_id of myPosts) {
await sleep(1000); // simulate fetch post
console.log('post:', post_id);
}
console.log('done!');
};
fetchPosts();
use async.eachOf to iterate over an array and apply async function :
async.eachOf(myPosts, function(post, it, callback){
// apply async function on each element
getPostAuthor(post.authorID, function(postAuthor) {
post.author = postAuthor;
return callback();
});
}, function(err){
// final callback when flow is finished
res.render('index', {
posts: myPosts
});
return res.end();
});
See async documentation : https://caolan.github.io/async/docs.html