Using Promises in Mongoose Routes

2019-06-02 08:02发布

问题:

I understand Promises in JavaScript or atleast I think I do (correct me if Im wrong).

Create a Promise:

var promise = new Promise(function(resolve, reject){
  // do something async here

  if(itWorked){

    resolve();

  }else {

    reject();
  }

});

Use a Promise:

promise.then(function(result){

},function(err){

});

I have an app based on MEAN Stack and some of my mongoose routes are becoming a callback hell. I want move away from callbacks in mongoose routes and handle them using Promises. But I can't seem to figure out how exactly to do that.

For example:

app.get('/users', function(req, res){

  User.findOne(_id, function(err, user){
      if(err)
         console.log(err)
      else if(user){

         Receipt.findOne(user.bookName, function(err, book){
           if(err)
            console.log(err)

           else if(book){
             // again do something here
          }  
       })
     }
   })
});

In the above example you can see that the code is getting harder to read and maintain. How can I use Promises here to improve my code?

回答1:

Ok, first are you using mongoose right?

User is your mongoose model which responds to methods such as findOne(query), findOneAndUpdate(query, update, opts) etc..

After calling any of these methods, call the method exec() which returns a Query.

A Query is not exactly a Promise and you'll need to specify which Promises will Mongo use. Here I would recommend to use the native ones so, add this line at your project:

 const mongoose = require('mongoose')
 mongoose.Promise = Promise

Now, this instruction is returning you a Promise, nice!

User.findOne(q).exec()

You can now create a Promise chain with your logic, for example:

 return User
     .findOne(_id)
     .exec()
     .then((user) => {
       return Receipt
         .findOne({ bookName: user.bookName })
         .exec()
       })
     .then((receipt) => {
            // example
            return res.status(200).json(receipt)
         }))
     })

Tip: I'd suggest to create aux methods in order to make the code even more cleaner:

/** @return {Promise} */
function findUser(id) {
  return User.findOne(id).exec()
}

/** @return {Promise} */
function findReceipt(bookName) {
  return Receipt.findOne({ bookName }).exec()
}

// example
function f(req, res) {
  const userId = req.get('id')

  return findUser(userId)
    .then((user) => findReceipt(user.bookName))
    .then((receipt) => res.status(200).json(receipt))
    .catch((err) => res.status(500).json(null))
}

// register your handler
app.get('/book', f)