承诺链未按预期顺序执行

Promise chain not executing in expected order

本文关键字:顺序 执行 承诺      更新时间:2023-09-26

我试图使用一系列promise来验证用户名和密码,但这些promise解决得太早了。

我有两种方法,它们来自两个不同的文件。

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

这是我的控制器:

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返回一个promise,如果用户不存在,它将抛出一个错误;如果找到用户,则解析并生成数据库行。(这就是为什么我有promise.catch调用。以防用户不存在)

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

不过,我遇到的问题是,我的控制器中的.then正在立即执行,而对orm.select的调用返回了初始承诺的结果。有人能解释为什么会发生这种情况,以及我该如何解决吗?

之所以会发生这种情况,是因为您已将回调直接连接到orm.select返回的promise。相反,您需要使用then返回的promise。

这是promise最大的关键之一:then返回一个新的promise。(catch也是。)如果处理程序函数从thencatch返回promise(从技术上讲,任何),则then(或catch)返回的新promise将根据您返回的promise进行解析/拒绝。如果处理程序函数返回非promise值,则then(或catch)中的新promise将使用该值进行解析(而不是拒绝)。

所以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中所做的更改。还要注意,无论是来自orm.select的原始promise被拒绝,还是来自compare的promise,都将调用catch回调中的代码。

要了解发生了什么,请将此片段与以下片段进行比较:

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

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

这样做是为了确保people.authenticate返回一个承诺链,该承诺链解析为比较后的内部结果:

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