3

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 ?

2
  • 1
    await not works on forEach use for or for of, more details here Commented Jul 12, 2022 at 12:31
  • @1sina1 , Oh okay that's what I did not know. I will try to use for or for of from now on when working with async operations. I got a better understanding now, thank you ! Commented Jul 12, 2022 at 15:29

3 Answers 3

2

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');

Sign up to request clarification or add additional context in comments.

1 Comment

Oh right, thank you for taking the time to explain. Now I have a better understanding of how async operations work. I tried the solution you provided and it's now working as expected, thanks !
1

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);

1 Comment

Thank you, I understand it better now. I'll try using a for loop instead. Kudos !
0

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 : []
}

1 Comment

Hey, thank you. And then i should call this function right after ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.