when I create my api in nodejs and trying to pushing mongoose return count to new created array, it's not wait for the forEach and execute json.res() and giving null response. when I use setTimeout() then it's giving proper result.
let newcategories = [];
let service = 0;
const categories = await Category.find({}, '_id name');
categories.forEach(async (category) => {
service = await Service.count({category: category});
newcategories.push({ count:service });
console.log('newcategories is -- ', newcategories);
}); /* while executing this forEach it's not wait and execute res.json..*/
console.log('result --- ',result);
console.log('out newcategories is -- ', newcategories);
res.json({status: 200, data: newcategories});
2 Answers 2
So the problem you have is that async marked functions will return a promise per default, but that the Array.prototype.forEach method doesn't care about the result type of your callback function, it is just executing an action.
Inside your async function, it will properly await your responses and fill up your new categories, but the forEach loop on categories will be long gone.
You could either choose to convert your statements into a for .. of loop, or you could use map and then await Promise.all( mapped )
The for..of loop would be like this
for (let category of categories) {
service = await Service.count({category: category});
newcategories.push({ count:service });
console.log('newcategories is -- ', newcategories);
}
the map version would look like this
await Promise.all( categories.map(async (category) => {
service = await Service.count({category: category});
newcategories.push({ count:service });
console.log('newcategories is -- ', newcategories);
}));
The second version simply works because Promise.all will only resolve once all promises have completed, and the map will return a potentially unresolved promise for each category
Comments
You need to use map instead of forEach, to collect the awaits and wait for them to complete. Edit: Or you can use for..of which is pretty neat (thanks other ppl)!
const categories = ['a', 'b', 'c'];
function getNextCategory(oldCategory) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(String.fromCharCode(oldCategory.charCodeAt(0)+1));
}, 1000);
});
}
async function blah() {
const categoryPromises = categories.map(getNextCategory);
const nextCategories = await Promise.all(categoryPromises);
console.log(nextCategories);
}
blah();
async function blah2() {
const nextCategories = [];
for (const category of categories) {
nextCategories.push(await getNextCategory(category));
};
console.log(nextCategories);
}
blah2();
2 Comments
for..of loop if you want to do serial requests.Explore related questions
See similar questions with these tags.
async(so it will automatically return aPromise). So theforEachis long done before all your awaits are ready. Just change theforEachinto afor let category of categoriesand await inside thefor..ofblockreduceto run this in series:categories.reduce(async (previous, category) => { await previous; service = await Service.count... }, null);