Home > database >  How to stop execution until function finishes
How to stop execution until function finishes

Time:11-08

I'm working on an e-commerce web app using NodeJS and MongoDB (Mongoose).
I have a get route that will show products in shopping cart, and I'm struggling because I need one function to complete before continuing with the rest of the code.
The function pushes items to an array, and then I try to render a view passing that array, but the array goes away empty.

Here's my code.

router.get('/cart', function (req, res) {
  User.findOne({ username: req.user.username }, function (err, doc) {
    // This is the array that needs to be passed
    const prodAndQty = [];
    const prodEntries = Object.entries(doc.shoppingCart);
    // This is the function that I need to complete before continuing
    prodEntries.forEach(function (entry) {
      Product.findOne({ id: entry[0] }, function (err, doc) {
        prodAndQty.push([doc, entry[1]]);
      });
    });
    if (err) { console.log(err); }
    else {
      // If I log here, the array is empty.
      console.log(prodAndQty);
      if (doc) {
        res.render('cart', { doc: doc, qty: prodEntries.length, products: prodAndQty });
      } else {
        res.render('cart', { doc: null, qty: 0, products: null });
      }
    }
  });
});

If I log the array before rendering, the array is empty.
Also, in the terminal, the array is logged even before the Mongoose queries executes.

Terminal log

If I log the array inside the function that is pushing, the array is filled correctly, but it's too late to be useful.

Any help would be greatly appreciated.

CodePudding user response:

You need something called as promise-based behavior for your code. Please refer :

  1. async functions
  2. promise

CodePudding user response:

Try something like this -

router.get('/cart', async function (req, res) {
    try {
        const doc = await User.findOne({ username: req.user.username });

        if(!doc) {
            res.render('cart', { doc: null, qty: 0, products: null });
        }

        const prodEntries   = Object.entries(doc.shoppingCart);

        // Instead of one by one searching, I used `in` operator (you can do one by one too, if you want)
        const entires       = prodEntries.map((prod) => {
            return prod[0];
        })

        const prodAndQty    = await Product.find({ id : { $in : entires }});
        // TODO - Modify this prodAndQty array as you want

        res.render('cart', { doc: doc, qty: prodEntries.length, products: prodAndQty });

    } catch(err) {
        throw new Error(err);
    }
}

In your approach, what happens is, you are using callback function which does not wait for the code to execute inside forEach and it just go ahead and returns the response.

Async-await is a syntactic sugar on the top of that which let you write code in a way, so it seems it's executing in serial manner. Another method is to use promises.

  • Related