Home > Back-end >  ForEach function not performing actions on each object, only once
ForEach function not performing actions on each object, only once

Time:04-17

Im building a Deck building application for a card game, i also have a database setup that i use to sell these cards i play with, this deck builder effectively allows me to take from the stock of the sellable stock (i.e. if i have 1 of x card, and i add that to a deck, it sets the countInStock of that item in the database to 1 less of what it was.) however when i delete the entire {deck} i need to re-seed into the sellable stock everything that was in [deck.cards] which is an array of {Card} objects, Im trying to simply loop over each object in that array, find that card, find the product in the database, set the product to 1 (as each card, can be the same card, but it will be a different object) therefore i simply just need to increase the countInStock by 1, when i hit this endpoint, it will place back in to stock 1 object, but it will seemingly ignore the other objects in the forEach loop and not add more to the count in stock, im not sure why itll work once, but it wont work over each iteration.

code:

/**
 *  @description  This function removes a deck from the database, and re-seeds whatever cards where in that deck
 *                back into the sellable stock
 *
 *  @route        DELETE /api/deck/:deckId
 *  @param        deckId ObjectID of the deck
 *  @comment      hitting this route will change the countInStock value of the {product}
 *                in the database by whatever cards where in the [deck.cards]
 *
 */
module.exports = asyncHandler(async (req, res) => {
  try {
    // find the deck
    const deck = await Deck.findById(req.params.id);
    // check if it exists
    if (!deck) {
      return res
        .status(404)
        .json({ message: `Deck: ${req.params.id} cannot be found` });
    }

    // we need to run a foreach command over every object in [deck.cards]

    await deck.cards.forEach(async (c) => {
      console.log(c);
      // find the card
      const card = await Card.findById(c._id);
      // We then need to find the sellable { Product } and add back to it, so it can be sold/traded
      const product = await Product.findById(card.productId);
      // increase the amount of inStock sellable items, by 1.
      await product.set({ countInStock: product.countInStock   1 });
      await product.save();
      // if we get here we want to remove the card object from the database, its just a placeholder.
      await card.remove();
    });

    // remove the deck
    await deck.remove();
    res.status(200).json({ success: true });
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: `Server Error: ${error.message}` });
  }
});

CodePudding user response:

I will try to describe the explanation given by @jfriend00 as much as possible.

Many including me thought that forEach is a simplified version of the for loop, but the truth is IT IS NOT when it comes to the async await. For example, let's take this code:

const loopThis = [1, 2, 3, 4];

function timeOut2000() {
  return new Promise(resolve => setTimeout(resolve, 1000));
}

async function loopThrough() {
  await loopThis.forEach(async x => {
    await timeOut2000();
    console.log(x)
  });
}

loopThrough()

When running the above, we can see that it doesn't wait for the timeOut2000 on each loop, because it doesn't wait for each promise to resolve.

To achieve the async await for each loop, we can use for loop or for .. in .. like below:

const loopThis = [1, 2, 3, 4];

function timeOut2000() {
  return new Promise(resolve => setTimeout(resolve, 1000));
}

async function loopThrough() {
  for (const x of loopThis) {
    await timeOut2000();
    console.log(x)
  };
}

loopThrough()

  • Related