【问题标题】:Promise chain not executing in expected orderPromise 链未按预期顺序执行
【发布时间】:2016-07-25 02:37:31
【问题描述】:

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

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

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

这是我的控制器:

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 将输入与数据库行中包含的哈希值进行比较。当异步完成时,我想解析承诺链并将控制权返回给控制器,返回 results 对象。

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

【问题讨论】:

    标签: javascript promise bluebird


    【解决方案1】:

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

    这是关于 Promise 的最重要的事情之一:then 返回一个新的 Promise。 (catch 也是如此。)如果您的处理函数从thencatch 返回一个promise(技术上,任何thenable),那么then(或catch)的新promise ) 退货将根据您返回的承诺解决/拒绝。如果您的处理函数返回一个非承诺值,来自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 中所做的操作。另请注意,无论是来自orm.select 的原始承诺被拒绝,还是来自compare 的承诺,都将调用catch 回调中的代码。

    要查看发生了什么,请将此 sn-p 与后面的进行比较:

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

    【讨论】:

    • @WilliamB:很重要,刚刚测试过。嗬!已修复,谢谢!
    • 从文档中并不清楚,我什至不确定为什么在这种情况下会调用 fn2。但是在 Promise 链的末尾有一个最终的“catch”是避免意外行为的最好方法,或者使用.then(successFn, errFn)
    • @WilliamB:是的,只是它涵盖了两个承诺。但是A)这通常是你想要的,B)它可能是OP想要的。所以...... :-) (每次我认为我理解了承诺,甚至有过“啊哈!”的时刻,我都会发现一些东西证明我还有更多的东西要学。它们看起来很......简单。)
    • @WilliamB 明确一点,我可以在链中的任何位置放置任意数量的catches,只是它们不会阻止链中的下一个承诺执行?
    • @LukeP,最好的答案是“仅将 catch() 放在链的末尾”,因为默认情况下,无论原始解决方案还是拒绝,它都会返回一个成功解决的承诺。 .catch 之后的任何 .then() 都将在 promise 失败时使用 undefined 调用,如果 promise 成功,将使用 promise 的解析值调用。最好避免这种模式
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2020-01-05
      • 1970-01-01
      • 2013-09-26
      • 2022-01-17
      • 2015-08-07
      • 1970-01-01
      • 2021-09-15
      • 1970-01-01
      • 2015-07-26
      相关资源
      最近更新 更多