Reputation: 47
I'm working on a NestJSxMongoose store management project.
I have this piece of code in which I want to update some items in the database and put those updated items in an array that I will use later.
const updatedItems: Item[] = [];
purchaseData.items.forEach(async (purchasedItem) => {
const itemInDB = await this.itemService.findItemByName(purchasedItem.name);
itemInDB.quantity -= purchasedItem.quantity;
const updatedItem = await this.itemService.updateItem(
itemInDB['_id'],
itemInDB
);
updatedItems.push(updatedItem);
console.log(updatedItems); // Output : [{actual_data}]
});
console.log(updatedItems); // Output : []
The issue I have is that when I log the content of updatedItems inside the forEach(), it contains the actual expected data. But when I try to use it outside the forEach() loop, it logs an empty array. I want to use that array outside of the forEach().
What am I doing wrong ?
Upvotes: 3
Views: 1339
Reputation: 23029
If you want to wait for results, you have to write it like this: Get the array of all promises and awaits them.
.forEach (as well as .map) runs synchronously. If you pass async function as a parameter, it does not await
them, it just executes them to the nearest await and then returns (still unresolved) promise.
Therefore the console.log(updatedItems); // Output : []
is executed before the logic inside async function.
const updatedItems: Item[] = [];
const promises = purchaseData.items.map(async (purchasedItem) => {
const itemInDB = await this.itemService.findItemByName(purchasedItem.name);
itemInDB.quantity -= purchasedItem.quantity;
const updatedItem = await this.itemService.updateItem(
itemInDB['_id'],
itemInDB
);
updatedItems.push(updatedItem);
console.log(updatedItems); // Output : [{actual_data}]
});
await Promise.all(promises);
console.log(updatedItems); // Output : []
Here you can see the order of execution:
const purchaseData = { items: [1,2] }
purchaseData.items.forEach(async (purchasedItem) => {
console.log('first', purchasedItem);
await console.log(); // the awaiting console log does nothing, it is just to trigger the beaviour of async function when it hits await
console.log('third', purchasedItem);
});
console.log('second');
Upvotes: 2
Reputation: 1444
forEach
expects a synchronous function. If an asynchronous callback is passed to it, the synchronous parts of the program (here, your second console.log
) will be executed before executing the callback awaited lines. Pay attention to this link.
It is better to put the whole iterative parts in an asynchronous function and use a for
loop instead of forEach
inside it, and then write the console.log
after calling the asynchronous function.
const updatedItems: Item[] = [];
async function updateData(){
for (let index = 0; index < purchaseData.items.length; index++) {
const itemInDB = await this.itemService.findItemByName(purchaseData.items[index].name);
itemInDB.quantity -= purchasedItem.quantity;
const updatedItem = await this.itemService.updateItem(
itemInDB['_id'],
itemInDB
);
updatedItems.push(updatedItem);
console.log(updatedItems);
}
}
await updateData();
console.log(updatedItems);
Upvotes: 1
Reputation: 65
Use Async and await its not waiting till the loop its going to next you need to wait until the loop finish
async function updateItemFun() {
const updatedItems: Item[] = [];
await purchaseData.items.forEach(async (purchasedItem) => {
const itemInDB = await this.itemService.findItemByName(purchasedItem.name);
itemInDB.quantity -= purchasedItem.quantity;
const updatedItem = await this.itemService.updateItem(
itemInDB['_id'],
itemInDB
);
updatedItems.push(updatedItem);
console.log(updatedItems); // Output : [{actual_data}]
});
console.log(updatedItems); // Output : []
}
Upvotes: 0