0

I am using q and I have multiple mongoose .exec() promises that never gets to the .then() part of the code, so never allow the q to resolve. Can't figure out why it never comes back.

    var defer = q.defer();
    var promises = [];
    console.log('Exams:', exams.length);
    for (var e=0; e<exams.length; e++) {
      console.log('Exams:', exams[e]._id);
      var newPromise = Pupilexam.find({ _exam: exams[e]._id }).populate('_user').exec()
        .then((pupils) => {
          console.log("Adding pupils", exams[e]._id);
          exams[e].pupils = pupils;
          resolve(exams[e]);
        })
        .catch((err) => {
          reject(err);
        });
      console.log(typeof newPromise);
      promises.push(newPromise);
      console.log("Promised pushed");
    }
    q.all(promises).then(function(data){
        console.log("q'd all");
        defer.resolve(res.status(200).json(exams));
    });
    return defer;

The Pupilexam.find().exec() never reaches the .then() so the promises never resolve and the defer never resolves. Why would the mongoose find not get to the .then()? What have I missed?

*** UPDATE ***

Even using the built in promises, we get the same issue. The Pupilexams.find() call never comes back.

    var promises = [];
    for (var e=0; e<exams.length; e++) {
      console.log('e:', e);
      console.log('Exam', exams[e]._id);
      var newPromise = Pupilexam.find({ _exam: exams[e]._id }).populate('_user').exec()
        .then((pupils) => {
          console.log("Adding pupils", exams[e]._id);
          exams[e].pupils = pupils;
        })
        .catch(handleError(res));
      promises.push(newPromise);
    }
    Promise.all(promises).then((exams) => {
      console.log(values);
      res.status(200).json(exams)
    });

With this method I also get a headers error on the call UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

** ADDITIONAL CODE REQUESTED **

function handleError(res, statusCode) {
    statusCode = statusCode || 500;
    return function(err) {
      console.log(err.message);
      res.status(statusCode).send(err);
    }; 
}
Ben Drury
  • 1,356
  • 2
  • 16
  • 34
  • May I ask if there's any particular reason for using `q`-promises? `Promise.all` is supported out of the box through native promises. – eol Nov 21 '20 at 13:39
  • Avoid the [deferred antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it) (`q.defer()`) and the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it) (`resolve(exams[e]);`, `reject(err);`)! – Bergi Nov 21 '20 at 14:37
  • your logic is not right. if understand you have an array of exams. you want each exam to add pupil's data to the exam object. each exam array is an array of objects, you can add to each object property including the pupil's info. if has an error from mongo you need to handle it, call it again. after that of having exams array with pupils info. without your logic. also if you just reading the data from mongo, recommended to use the lean function, – Omer Cohen Nov 21 '20 at 15:54
  • @eol habit. I've used it elsewhere. But I get the same issue when I use promises.all. The mongoose calls in the loop never return and run the `.then()` code. – Ben Drury Nov 22 '20 at 07:21
  • @Bergi I have updated with promises.all to avoid, but get the same issue. – Ben Drury Nov 22 '20 at 07:22
  • @BenDrury Can you post your `handleError` implementation, please? It seems eol's answer is right. Also, for some reason you are getting an error, but we can't tell you how to fix that without more details about input and the error. – Bergi Nov 22 '20 at 12:14
  • Have added handleError. The error was `Cannot read property 'Id' of undefined` because `exams` has two objects in the array and the `e` had reached 2 before the promise returned, so it errored. – Ben Drury Nov 22 '20 at 14:30

4 Answers4

2

To answer the updated question regarding the Cannot set headers after they are sent to the client error. Looks like you send a response to the client inside your handleError function. Now, if more than one Pupilexam.find call fails, handleError would be invoked twice, resulting in the mentioned error.

You should move the catch-handler down to the Promise.all call:

const promises = [];
for (const exam of exams) {
    const newPromise = Pupilexam
        .find({ _exam: exam._id }).populate('_user').exec()
        .then((pupils) => {
            exam.pupils = pupils;
        });
    promises.push(newPromise);
}
Promise.all(promises)
  .then((exams) => {
    res.status(200).json(exams);
   }) 
  .catch(handleError(res));
eol
  • 23,236
  • 5
  • 46
  • 64
0

I guess that you are indeed returning your promise but you are returning an empty json. There are 2 problems with your approach:

  1. You are not returning from your then: should return pupils and it is returning undefined

  2. You are logging values that I don't know what it is

     .then((pupils) => {
       console.log("Adding pupils", exams[e]._id);
       exams[e].pupils = pupils; 
       // you should return something // return pupils
     })
     promises.push(newPromise);             
    
     Promise.all(promises).then((exams) => {
     // ['undefined', 'undefined', ...]
           console.log(values);
           res.status(200).json(exams)
         });
    
iwaduarte
  • 1,600
  • 17
  • 23
  • I was deliberately not returning anything, as I wanted nulls to return while I was testing, but yes, I will need a returned value for the `promise.all` to send back. Which values are you looking for? `exams` is an array of objects from mongo. – Ben Drury Nov 22 '20 at 14:25
-1

Looks like the answer was that on these two lines the exams[e] is not in scope, because by the time the promise comes back the loop has moved on, so e is wrong and gets too high so it was erroring.

      console.log("Adding pupils", exams[e]._id);
      exams[e].pupils = pupils;

Only discovered that when I read @eol's message about the catch and decided to catch it properly and output.

Ben Drury
  • 1,356
  • 2
  • 16
  • 34
  • there is nothing to do with that, I have answered correctly though. – iwaduarte Nov 22 '20 at 14:10
  • The issue was that the loop was changing the value of e before the promise returned, so it was out of sync with the relevant exam in the array, which caused a problem. – Ben Drury Nov 22 '20 at 14:22
-2

it is Look from your code.

    //(async) function
    for (var e of exams) {
        try {
         const pupils = await  Pupilexam.find({ _exam: exams[e]._id 
                }).populate('_user').exec().lean()
          e.pupils = pupils
             
        }catch((err){
          //handleError
        };
    }
res.status(200).json({data: exams})
    

maybe that will show you how match are you wrong dicription what are you dicribe

Omer Cohen
  • 132
  • 1
  • 10
  • 1
    I don't believe this will work. The api will return before all the mongoose calls are back and have populated the exams, so I won't get the data I want. – Ben Drury Nov 22 '20 at 07:23
  • do you know then website Join => https://www.join.me/remote-work. I want to prove myself. when you can ? – Omer Cohen Nov 22 '20 at 19:09