【问题标题】:How to chain Promises and callback-style code如何链接 Promises 和回调样式的代码
【发布时间】:2018-03-07 23:54:06
【问题描述】:

我对这个 Promise 链接的工作原理感到困惑,我对 Promise 和 js 还很陌生,所以请原谅

第三行,return user.findOne({email}).then((user) => {,我只是对返回这个承诺如何做任何事情感到困惑,因为它在 .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()
          }
        });
    });

  });
}

在 express 应用中使用的 findByCredentials 模型方法

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("!");
  })

我刚刚创建的一个更简单的例子,这部分

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

return new Promise((resolve, reject) => { 是我无法理解的问题

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);
});

【问题讨论】:

  • then回调返回的承诺是the true power of promises
  • @Bergi 不想打扰你,但你是专家;你会用另一种方式写这个,不同于我在回答中描述的方式吗?我也很想知道。
  • @NicholasKyriakides 你的意思是解释还是代码?关于代码,我不会使用new Promise 作为bcrypt natively supports returning promises :-)
  • @Bergi Nice;我会将其添加为注释,但保持结构不变以说明如何处理 Promise 链中的回调样式代码。
  • @NicholasKyriakides 实际上,我真的很喜欢您答案的原始修订版,您将其分解为辅助函数。使承诺代码更易读 imo

标签: javascript promise return


【解决方案1】:

正如@Bergi 在您的 OP 评论中所说,真正的力量或Promises 来自于在其他Promisesthen 中返回它们。

  • 这允许您以一种干净的方式链接 Promise。
  • 要链接 Promises,链中的所有操作必须是 Promises。
  • 您的bcrypt.compare 函数使用回调来表示它已完成,因此您需要将该函数转换为Promise

这很容易做到。只需将回调样式代码包装在 Promise 中,并将 resolve 包装为回调的 resultreject,如果使用 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)
    })
  })   
}

...然后我们可以正确链接:

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
        })
    })
}

这里的关键是.then() 中的任何return 都将作为参数传递给下一个链接的.then()

附加信息:

  • bcrypt.comparealready returns a Promise,所以我们可以避免将它包装成 Promise 的整个麻烦。我特意将它与回调一起使用,以说明应如何处理 Promise 链中的回调样式代码。

【讨论】:

  • 抱歉,如果我没有明确表达我的意图,我并不是真的在寻找解决方案,更多的是解释它为什么/如何工作
  • 你这样做是正确的;让我编辑和解释。我的错。
  • 我添加了一个更简单的例子来解决你是否更容易回答
猜你喜欢
  • 1970-01-01
  • 2021-10-19
  • 2017-05-09
  • 2014-12-13
  • 2020-04-29
  • 2011-02-11
  • 2019-09-28
  • 2016-02-19
  • 2015-11-20
相关资源
最近更新 更多