你能避免和Promises一起嵌套吗

Can you avoid nesting altogether with Promises?

本文关键字:嵌套 一起 Promises 能避免      更新时间:2023-09-26

据我所知,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');
        }
    });
}

是的,您总是可以使用Promise.all(Bluebird中Promise.join的简写)来压平promise链,方法是使用promise作为代理。毕竟,promise是抽象值,你可以随时打开promise,并让其他变量依赖它

它是否更可读是另一个争论:

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循环和嵌套,而不是绕过这个选项并使用单个循环。