根据我的理解,Promises的主要卖点之一是能够编写扁平代码(或者比回调地狱更平坦)。
虽然看起来在很多情况下我们需要嵌套promises,以便使用闭包。例如(来自q的文档,虽然我使用Bluebird):
function authenticate() {
return getUsername()
.then(function (username) {
return getUser(username);
})
// chained because we will not need the user name in the next event
.then(function (user) {
return getPassword()
// nested because we need both user and password next
.then(function (password) {
if (user.passwordHash !== hash(password)) {
throw new Error("Can't authenticate");
}
});
});
}
有没有更简洁的方法来做到这一点,没有嵌套?
编辑:我已经设法使用.all
清理了这个具体示例,但是有一些更复杂的情况我不认为可以做到:
function authenticate() {
return Promise.all([
getUsername().then(getUser),
getPassword()
]).spread(function (user, password) {
if (user.passwordHash !== hash(password)) {
throw new Error('Can\'t authenticate');
}
});
}
答案 0 :(得分:6)
是的,您可以使用Promise.all
(通过Bluebird中的Promise.join
缩写)通过使用它们的代理承诺来展平承诺链。毕竟 - 承诺抽象值,您可以随时解开一个承诺,并让其他变量依赖它。
它是否更具可读性是另一个争论:
foo().then(function(a){
return bar(a).then(function(b){
return g(a,b); // "needed" to nest because I wanted `a`
});
});
可以写成:
var a = foo();
var b = a.then(bar);
Promise.join(a, b, function(a,b){
return g(a, b); // alternatively, res could have been `Promise.join(a,b, g)`
});
所以一般情况下 - 你可以总是避免嵌套,但是很多时候你可能不想这样做。
在您的情况下,甚至可以是:
function authenticate() {
var userPass = Promise.all([ getUsername().then(getUser), getPassword()]);
var passHash = userPass.get(0).get("passwordHash");
var newHash = userPass.get(1).then(hash);
var equal = Promise.join(userHash, newHash, function(a, b){ return a !==b });
return equal.then(function(val){ if(!val) throw new Error("..."); });
}
取悦?当然。更好?这是另一个问题。如果你有一个嵌套的for
循环,你可能希望保持嵌套for
循环并嵌套而不是破解该选项并使用单个循环。