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。
这是关于承诺的最重要的事情之一:then
return是一个新的承诺。 (catch
也是如此。)如果您的处理程序函数 return 是来自 then
或 catch
的承诺(从技术上讲,任何 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);
});
我正在尝试使用一系列承诺来验证用户名和密码,但承诺解决得太早了。
我有两个方法来自这里涉及的两个不同的文件。
首先是我的控制器,它接收请求并调用我的模型进行身份验证。该模型应该 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。
这是关于承诺的最重要的事情之一:then
return是一个新的承诺。 (catch
也是如此。)如果您的处理程序函数 return 是来自 then
或 catch
的承诺(从技术上讲,任何 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);
});