1

I'm confused on how this chaining for promises work, I'm still fairly new to promises and js in general so excuse me

line three, return user.findOne({email}).then((user) => {, i'm just confused about how returning this promise does anything since it returns a other promise inside the .then()

UserSchema.statics.findByCredentials = function(email, password){
  user = this;
  return user.findOne({email}).then((user) => {
      if (!user){
        return Promise.reject();
      }
      return new Promise((resolve, reject) => {
        bcrypt.compare(password, user.password, (err, res) => {
          if (res){
            resolve(user);
          }else{
            reject()
          }
        });
    });

  });
}

the findByCredentials model method being used in an express app

app.post("/users/login", (req, res) => {
  var body = _.pick(req.body, ["email", "password"]);
  User.findByCredentials(body.email, body.password).then((user) => {
    res.send(body)
  }).catch((e) => {
    res.send("!");
  })

A simpler example I just created, this part

return plus(1).then((res) => {

return new Promise((resolve, reject) => { is the problem i'm having trouble understanding

function plus(a) {
  return new Promise((resolve, reject) => {
    resolve(a + 1);
  });
}

function test() {
  return plus(1).then((res) => {
    console.log(res);
    return new Promise((resolve, reject) => {
      resolve("Test");
    });
  });
}

test().then((res) => {
  console.log(res);
});
nicholaswmin
  • 21,686
  • 15
  • 91
  • 167
  • 2
    Returning promises from `then` callbacks is [the true power of promises](https://stackoverflow.com/a/22562045/1048572) – Bergi Mar 08 '18 at 00:14
  • 1
    @Bergi Hate to bother you but you're the expert; Would you write this in another way, different than the one I've described in my answer? I'm curious to know as well. – nicholaswmin Mar 08 '18 at 00:28
  • @NicholasKyriakides you mean the explanation or the code? Regarding the code, I would not have used `new Promise` as [`bcrypt` natively supports returning promises](https://www.npmjs.com/package/bcrypt#with-promises) :-) – Bergi Mar 08 '18 at 00:46
  • @Bergi Nice; I'll add it as a comment but keep the structure as-is to illustrate how to handle callback-style code in a Promise chain. – nicholaswmin Mar 08 '18 at 00:47
  • 1
    @NicholasKyriakides Actually I really liked the original revision of your answer where you had factored it out into a helper function. Makes promise code much more readable imo – Bergi Mar 08 '18 at 00:49

1 Answers1

3

As @Bergi said in the comment of your OP, the true power or Promises comes from returning them in the then of other Promises.

  • This allows you to chain Promises in a clean way.
  • To chain Promises all your operations in the chain must be Promises.
  • Your bcrypt.compare function uses callbacks to signal that it's done, so you need that function convert to a Promise.

This is easy to do. Just wrap the callback-style code in a Promise and resolve the result of the callback or reject if the callback is called with an err.

const comparePassword = (a, b) => {
  return new Promise((resolve, reject) => {
    bcrypt.compare(a, b, (err, result) => {
      // Reject if there was an error
      // - rejection is `return`-ed solely for stopping further execution
      //   of this callback. No other reason for it.
      if (err) return reject(err)

      // Resolve if not.
      resolve(result)
    })
  })   
}

... and then we can chain properly:

UserSchema.statics.findByCredentials = function(email, password) {
  // Outer Promise: 
  // - Will eventually resolve with whatever the result it's inner
  //   promise resolves with.
  return user.findOne({ email })
    .then((user) => {
      // Inner Promise: 
      // - Will eventually resolve with `user` (which is already
      //   available here), given that the password was correct, 
      //   or 
      //   reject with the bcrypt.compare `err` if the password was 
      //   incorrect. 
      return comparePassword(password, user.password)
        .then((result) => {
          // This `then` belongs to the comparePassword Promise.
          // - We use this so we can make sure we return the `user` we picked up
          //   from the previous `user.findOne` Promise.
          // - This ensures that when you chain a `then` to this Promise chain
          //   you always get the `user` and not the result of `comparePassword`
          return user
        })
    })
}

The key here is that whatever you return within a .then() is going to be passed as an argument to the next chained .then().

Additional info:

  • bcrypt.compare already returns a Promise, so we could have avoided the whole hassle of wrapping it into a Promise. I've intentionally used it with callbacks to illustrate how you should handle callback-style code in a Promise chain.
nicholaswmin
  • 21,686
  • 15
  • 91
  • 167
  • Sorry if I didn't make my intentions clear, i'm not really looking for a fix, more of an explanation of why/how it works –  Mar 08 '18 at 00:15
  • And you would be correct to do that; Let me edit and explain. My bad. – nicholaswmin Mar 08 '18 at 00:16
  • I added a simpler example to work off of if it'd be easier for you to answer –  Mar 08 '18 at 00:19