【问题标题】:How to turn nested callback into promise?如何将嵌套回调变成承诺?
【发布时间】:2017-06-18 08:33:36
【问题描述】:

最近我开始使用 pg-promise 和 bluebird 库。我一直在嵌套回调并在每个回调中处理err。我发现 promise 中的 catch 语句看起来非常简洁。我不确定是否可以将此代码转换为承诺基础?

username = username.toUpperCase();
let text = "SELECT * FROM users WHERE username = $1";
let values = [username];
database.one(text, values).then(function (userObject) {
  // ANY WAY TO TURN this nested bycrypt into promise chain?
  bcrypt.compare(password, userObject.password, function (err, same) {
    if (err) {
      return next(err, null);
    }
    if (!same) {
      return next(new Error("Password mismatched!"), null);
    }
    const serializeObject = {_id: userObject._id};
    return next(null, serializeObject);
  });
}).catch(function (err) {
  return next(err, null);
});

【问题讨论】:

  • bluebird 有一个promisify function,它会为你承诺bcrypt.compare
  • @JaromandaX 是的,我正在阅读,所以我所要做的就是在函数末尾添加“Async”关键字?
  • 是的,我想,有点,一旦它被承诺 - 阅读文档:p
  • I am not sure if it possible to turn this code to promise base 任何代码都可以变成promise,而promisifybcrypt 模块解决方案是最简单的。

标签: javascript node.js promise bluebird pg-promise


【解决方案1】:

这是对@Jaromanda 答案的扩展,以防您只使用一个功能,并且只想看看如何手动承诺它。

function samePassword(password1, password2) {
    return new Promise(function (resolve, reject) {
        bcrypt.compare(password1, password2, (err, same) => {
            err = err || (!same && new Error("Password mismatched!"));
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });

    });
}

db.one(text, values)
    .then(userObject => {
        return samePassword(password, userObject.password);
    })
    .catch(error => {
        return next(error, null);
    });

除此之外,promisify 方法是可行的方法。但是了解它的有效作用总是好的;)

【讨论】:

  • 谢谢你,这让我对 Promisify 有了更多的了解。在您的一份文档github.com/vitaly-t/pg-promise/wiki/Common-Mistakes 中,您提到我们应该避免使用 Promise.all 并用 ` // 不解决查询;` 您能否提供更多解释为什么它不解决?最终不是查询一个承诺函数,应该与 Promise.all 一起工作?
  • 这不是关于promise 函数不起作用,而是关于Promise.all 不保证按照其协议解决promise,即Promise.all 只保证在方法本身解决时解决所有promise。
  • 我明白了,Promise.all 和 Promise 链接同一个东西吗?我将详细阅读 Promise,但希望得到一些快速的答案:)
  • Promise.all 是一种“等待”多个承诺来解决的方法......但如果只有一个拒绝,Promise.all 将拒绝
  • @vitaly-t - 干得好,我也在考虑展示如何承诺该功能,但我的回答已经很冗长了:p
【解决方案2】:

我想象使用 bluebirds promisify,你会像这样 promisify bcrypt.compare(你不必使用名称的 Async 部分)

let compareAsync = Promise.promisify(bcrypt.compare);

因为第一个 .then 中的 userObject 需要在第二个 .then 中使用,所以不能简单地通过返回 compareAsync 链接 .then,因为这样下一个 .then 将无法访问 userObject

一个解决方法是使用一个变量,该变量将在两个 .then 的范围内(但是 ugh)

username = username.toUpperCase();
let text = "SELECT * FROM users WHERE username = $1";
let values = [username];
let uo; //hacky the outer scoped variable
database.one(text, values).then(function (userObject) {
    uo = userObject;
    return compareAsync(password, userObject.password);
}).then(function(same) {
    if (!same) {
        throw new Error("Password mismatched!");
    }
    const serializeObject = {_id: uo._id};
    return next(null, serializeObject);
}).catch(function (err) {
    return next(err, null);
});

另一个(在我看来更简洁)选项是嵌套的 .then

username = username.toUpperCase();
let text = "SELECT * FROM users WHERE username = $1";
let values = [username];
database.one(text, values).then(function (userObject) {
    return compareAsync(password, userObject.password)
    // [optional] following three lines to generate a "nicer" error for compare failure
    .catch(function(err) {
        throw "bcrypt.compare failed";
    })
    // nested .then to pass on the userObject and same at the same time
    .then(function (same) {
        return { same: same, userObject: userObject };
    });
}).then(function (result) {
    let same = result.same,
        userObject = result.userObject;

    if (!same) {
        throw new Error("Password mismatched!");
    }
    let serializeObject = { _id: userObject._id };
    return next(null, serializeObject);
}).catch(function (err) {
    return next(err, null);
});

注意:bluebird 有一个 promisifyAll 函数......它在一个对象中承诺函数,并(默认情况下)将 Async 后缀添加到函数名称 - 我相信您可以决定不同的后缀名称,但文档会告诉你更多

当承诺单个函数时,您自己声明名称 - 上面可以很容易地实现

let trumpIsBigly = Promise.promisify(bcrypt.compare);

那么您只需使用trumpIsBigly,其中代码为compareAsync

最后一种可能性

一个手卷承诺的 compareAsync(主要来自vitally-t的答案,但有一些补充)

function compareAsync(password1, password2, inValue) {
    return new Promise(function (resolve, reject) {
        bcrypt.compare(password1, password2, function (err, same) {
            err = err || (!same && new Error("Password mismatched!"));
            if (err) {
                reject(err);
            } else {
                resolve(inValue);
            }
        });

    });
}

现在 compareAsync 将解析到传入值 inValue 只有在没有错误的情况下,AND也是如此

username = username.toUpperCase();
let text = "SELECT * FROM users WHERE username = $1";
let values = [username];
database.one(text, values).then(function (userObject) {
    return compareAsync(password, userObject.password, userObject)
}).then(function (userObject) {
    let serializeObject = { _id: userObject._id };
    return next(null, serializeObject);
}).catch(function (err) {
    return next(err, null);
});

这让“链”变得非常简单!

【讨论】:

  • 真正回答了我的大部分疑惑! promisify 函数看起来非常复杂。对不起,如果这些是荒谬的问题,但是如果库中已经有 Async 后缀怎么办?这种转换真的有多稳定?它会因某些功能而中断吗?
  • The promisify function seems really complicated - 真的吗?传入函数名......取回一个返回承诺的函数......你需要它有多简单?
  • 对不起:P 对 Promise 来说真的很陌生。另一个简单的问题,对于.catch(function(err))err 是否属于第一个出现的err?我目前正在阅读承诺以进一步了解它:)
  • yes (and no) ... 在这段代码中,第一个错误将触发 catch,如果它在 database.one 中,那么所有 .then 代码都不会运行跨度>
  • promisify 没有说明传入一个已经基于 promise 的函数。所以在我承诺任何函数之前,我必须确保它是一个正常的回调函数?通过阅读源代码等
猜你喜欢
  • 1970-01-01
  • 2014-07-05
  • 1970-01-01
  • 2018-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多