Promise 链未按预期顺序执行

Promise chain not executing in expected order

我正在尝试使用一系列承诺来验证用户名和密码,但承诺解决得太早了。

我有两个方法来自这里涉及的两个不同的文件。

首先是我的控制器,它接收请求并调用我的模型进行身份验证。该模型应该 return 一个承诺,在它解析时将有身份验证结果。

这是我的控制器:

router.post('/api/auth-tokens', function (req, res, next) {
    var creds = {user: req.body.user, password: req.body.password};
    people.authenticate(creds)
    .then(function (result) {
      var status = result.valid ? 200: 401; 
      res.sendStatus(status);
    });
});

people 是词法范围模型。这是上面使用的方法的当前实现:

var bluebird = require('bluebird');
var bcrypt   = require('bcrypt');
var compare  = bluebird.promisify(bcrypt.compare);

exports.authenticate = function (args) {
    var promise = orm.select({
        db: db,
        table: table,
        where: {id: args.user},
        qrm: 'one'
    });

    promise.catch(function (err) {
        var auth_info = { valid: false };
        return auth_info;
    });

    promise.then(function (user) {
        var promise = compare(args.password, user.hash)
        .then(function (same) {
            var auth_info = { valid: same, permissions: user.permissions };
            return auth_info;
        });
        return promise;
    });

    return promise;
};

orm return 是一个承诺,如果用户不存在将抛出错误,或者如果找到用户则解析并生成数据库行。 (这就是为什么我在那里有 promise.catch 调用的原因。以防用户不存在)

如果用户确实存在,我想调用 bcrypt.compare 将输入与数据库行中包含的散列进行比较。当异步完成时,我想解析承诺链并将控制权交还给控制器,returning results 对象。

我遇到的问题是,我的控制器中的 .then 正在立即执行,并立即执行调用 orm.select 的初始承诺 return 的结果。谁能解释为什么会这样以及我该如何解决?

您的控制器执行的承诺不同于使用 bcrypt.compare

的承诺

这样做是为了确保 people.authenticate returns 一个解析为内部结果的承诺链 post-比较:

var promise = orm.select(/* ... */)
    .then(/* bcrypt usage here */)
    .catch(/* error handling here */);

return promise;

之所以发生,是因为您已将该回调直接连接到由 orm.select 编写的 return 承诺。相反,您需要使用由 then 编辑的承诺 return。

这是关于承诺的最重要的事情之一:thenreturn是一个新的承诺。 (catch 也是如此。)如果您的处理程序函数 return 是来自 thencatch 的承诺(从技术上讲,任何 thenable) ,then(或catch)return的新承诺将resolve/reject基于你return的承诺。如果您的处理程序函数 return 是一个非承诺值,则来自 then(或 catch)的新承诺将使用该值解决(而不是拒绝)。

所以 authenticate 应该是这样的:

exports.authenticate = function (args) {
    return orm.select({
        db: db,
        table: table,
        where: {id: args.user},
        qrm: 'one'
    })
    .then(function (user) {
        return compare(args.password, user.hash)
        .then(function (same) {
            var auth_info = { valid: same, permissions: user.permissions };
            return auth_info;
        });
    })
    .catch(function (err) {
        var auth_info = { valid: false };
        return auth_info;
    });
};

请注意,这些更改既针对您的外部函数,也针对您在 then 中所做的事情。另请注意,您的 catch 回调中的代码将被调用,无论是来自 orm.select 的被拒绝的原始承诺,还是来自 compare.

的承诺

要了解发生了什么,请将此代码段与后面的代码段进行比较:

// Returning the original promise
// Usually NOT what you want
function foo() {
  var promise = new Promise(function(resolve) {
    console.log("Starting first promise");
    setTimeout(function() {
      console.log("Resolving first promise");
      resolve("first");
    }, 500);
  });
  promise.then(function() {
    promise = new Promise(function(resolve) {
      console.log("Starting second promise");
      setTimeout(function() {
        console.log("Resolving second promise");
        resolve("second");
      }, 500);
    });
  });
  return promise;
}
foo().then(function(result) {
  console.log("Got result: " + result);
});

第二个:

// Returning the original promise
// Usually NOT what you want
function foo() {
  return new Promise(function(resolve) {
    console.log("Starting first promise");
    setTimeout(function() {
      console.log("Resolving first promise");
      resolve("first");
    }, 500);
  })
  .then(function() {
    return new Promise(function(resolve) {
      console.log("Starting second promise");
      setTimeout(function() {
        console.log("Resolving second promise");
        resolve("second");
      }, 500);
    });
  });
}
foo().then(function(result) {
  console.log("Got result: " + result);
});