2

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

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

2 回答 2

4

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

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

因为userObject在第一个 .then 中需要在第二个 .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);
            }
        });

    });
}

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

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

这使得“链”非常简单!

于 2017-02-01T14:48:25.033 回答
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方法是要走的路。但是了解它的有效作用总是好的;)

于 2017-02-01T15:50:05.427 回答