Issue with mongoose.save never returning inside of promise

Viewed 66

Update !!

I fixed my initial issue with the help of Dacre Denny answer below however when writing tests for my code it turned out that the changes were not being saved before the server responded therefor the company collection in my test database was empty, I fixed this issue with the following code

  Companies.find({ company_name: company.company_name }).then(found => {
    if (found.length !== 0) {
      return res.status(400).json({ error: "Company already exists" });
    }

    var userForms = company.users;
    company.users = [];
    const finalCompany = new Companies(company);
    console.log(finalCompany);

    var userPromises = [];
    for (var x = 0; x < userForms.length; x++) {
      var user = userForms[x].user;
      user.company = finalCompany._id;
      userPromises.push(userCreation(user));
    }
    return Promise.all(userPromises).then(responses => {
      for (var x in responses) {
        if (!responses[x].errors) {
          finalCompany.addUser(responses[x]._id);
        } else {
          res.status(400).json(responses[x]);
        }
      }
      return finalCompany;
    });
  })
  // I moved the save in here !!!
  .then((finalCompany) => {
      finalCompany.save().then(()=>{
        res.status(200).json({signup:"Successful"});
      })
  },(err) => {
      res.json({error: err});
  });
});

Original Issue

I am trying to create a mongoose document to represent a company, this code saves the model in my db however it does not seem to be responding with a status code or reply to postman when I make a request

I've used a debugger to step through the code but I am very rusty on my JS and I am afraid I've gone into deep water with promises thats gone over my head.

router.post('/c_signup', auth.optional, (req, res, next) => {

  const { body: { company } } = req;

  var error_json = cbc(company);

  if( error_json.errors.length > 0 ){
    return res.status(422).json(error_json);
  }

  Companies.find({company_name: company.company_name})
  .then((found) => {
    if (found.length !== 0) {
      return res.status(400).json({error: "Company already exists"});
    }

    var userForms = company.users;
    company.users = [];
    const finalCompany = new Companies(company);

    var userPromises = [];
    for (var x =0; x < userForms.length; x ++) {
      var user = userForms[x].user;
      user.company = finalCompany._id;
      userPromises.push(userCreation(user));
    }

    Promise.all(userPromises).then((responses) => {
      for (var x in responses){
        if (!responses[x].errors){
          finalCompany.addUser(responses[x]._id);
        }
        else {
          res.status(400).json(responses[x]);
        }
      }
      console.log("h2");
      finalCompany.save(function () {
        console.log("h3");
        return res.status(200);
      });
    })
  });

  return res.status(404);
});

This is the output from the debug but the execution is hanging here

h2
h3
1 Answers

There are a few issues here:

First, the save() function is asynchronous. You'll need to account for that by ensuring the promise that save() returns, is returned to the handler that it's is called in.

The same is true with the call to Promise.all() - you'll need to add that promise to the respective promise chain by returning that promise to the enclosing handler (see notes below).

Also, make sure the request handler returns a response either via res.json(), res.send(), etc, or by simply calling res.end(). That signals that the request has completed and should address the "hanging behaviour".

Although your code includes res.json(), there are many cases where it's not guaranteed to be called. In such cases, the hanging behaviour would result. One way to address this would be to add res.end() to the end of your promise chain as shown below:

Companies.find({ company_name: company.company_name }).then(found => {
  if (found.length !== 0) {
    return res.status(400).json({ error: "Company already exists" });
  }

  var userForms = company.users;
  company.users = [];
  const finalCompany = new Companies(company);

  var userPromises = [];
  for (var x = 0; x < userForms.length; x++) {
    var user = userForms[x].user;
    user.company = finalCompany._id;
    userPromises.push(userCreation(user));
  }

  /* Add return, ensure that the enclosing then() only resolves
after "all promises" here have completed */
  return Promise.all(userPromises).then(responses => {
    for (var x in responses) {
      if (!responses[x].errors) {
        finalCompany.addUser(responses[x]._id);
      } else {
        res.status(400).json(responses[x]);
      }
    }
    console.log("h2");

    /* Add return, ensure that the enclosing then() only resolves
    after the asnyc "save" has completed */
    return finalCompany.save(function() {
      console.log("h3");
      return res.status(200);
    });
  });
})
.then(() => {
    res.end();
},(err) => {
    console.error("Error:",err);
    res.end();
});
Related