0

I'm new to javascript and i'm having a hard time making my response return to wait for my mongodb query finish running inside a forEach loop.

My code is currrently:

exports.applyThesaurus = (req, res, next) => {
  let numModified = 0;
  var prom = new Promise((resolve,reject) => {
   req.file.forEach((obj,idx) => {
    wos.updateMany(
      { creator: req.userData.userId},
      { $set: { [req.body.field+".$[element]"] : obj.replaceWith } },
      { arrayFilters: [ {  "element": { $in:  obj.replaced } } ] }
    ).then((result) => {
      console.log(result.nModified)
      numModified += result.nModified
    })
    .catch((err) => {
      res.status(500).json('There was an error while applying the thesaurus.');
    })

      if( idx === req.file.length -1) {
        resolve()
      }
   })
 })
 prom.then(() => {
  console.log('im returning');
  res.status(200).json({message: numModified + ' itens replaced successfully'});
 })
}  

What happens is that the "i'm returning" console log triggers before the one logging result.nModified

I need to be able to run all the updateMany queries and then respond with the number of updated itens.

Any tips? Thank you very much!

jay2p
  • 75
  • 6

1 Answers1

0

your code is trying to return resolve before your updateMany executes.

if (idx === req.file.length - 1) {
  resolve() // this resolve get's executed befour updateMany get executed
}

this might give you a better understanding of the callbacks and why it is happening. as said if you want to resolve the promise after your updateMany finishes execution you need to update your code as below:

exports.applyThesaurus = (req, res, next) => {
    let numModified = 0;
    var prom = new Promise((resolve, reject) => {
        let updateManyPromisesArray = []
        req.file.forEach((obj, idx) => {
            updateManyPromisesArray.push(wos.updateMany({
                creator: req.userData.userId
            }, {
                $set: {
                    [req.body.field + ".$[element]"]: obj.replaceWith
                }
            }, {
                arrayFilters: [{
                    "element": {
                        $in: obj.replaced
                    }
                }]
            }))

            Promise.all(updateManyPromisesArray)
                .then((result) => {
                    if (idx === req.file.length - 1) {
                        resolve()
                    }
                })
                .catch((err) => {
                    res.status(500).json('There was an error while applying the thesaurus.');
                })
        })
    })


    prom.then(() => {
        console.log('im returning');
        res.status(200).json({
            message: numModified + ' itens replaced successfully'
        });
    })
}

Also, you should start using async and await for avoiding these kinds of callback hells situations.

rohanraj
  • 392
  • 2
  • 12
  • Thanks for your reply but it still doesn't fully wait for all the updateMany queries. Before it would execute the "im returning" first but now it executes after 3~5 queries ran but not after all. I'll take a look in the async await you sent, i also tried some with it but no luck. – jay2p Jun 19 '21 at 02:28
  • have you tried using [Promise.all()](https://stackoverflow.com/questions/38180080/when-to-use-promise-all) ?@jay2p – rohanraj Jun 19 '21 at 02:45
  • also, try running the above snippet I have updated it with the `Promise.all()` – rohanraj Jun 19 '21 at 02:47
  • Hey, looks like its now executing the "im returning" last. I added the numModified += resut.nModified back before the IF with the resolve (i think you accidentaly removed it) but now its always undefined. EDIT: Just realized the result is now an array, i'll loop through it and sum the numbers. Thank you very much. – jay2p Jun 19 '21 at 02:58
  • it's returning `undefined` because `result` is not an object anymore. it's an array of objects. so you need to loop through `result` and handle your success and errors accordingly. – rohanraj Jun 19 '21 at 03:03