Can you avoid nesting altogether with Promises? [duplicate]

隐身守侯 提交于 2020-01-13 03:48:18

问题


From what I understand one of the main selling points for Promises is the ability to write flat code (or, flatter than callback hell).

Though it seems that in many cases we need to nest promises, in order to use closure. For example (from q's docs, though I use 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");
                    }
                });
        });
}

Is there a cleaner way to do this, without nesting?

EDIT: I've managed to clean up this specific example using .all, but there are more complex cases where I don't think it can be done:

function authenticate() {
    return Promise.all([
        getUsername().then(getUser),
        getPassword()
    ]).spread(function (user, password) {
        if (user.passwordHash !== hash(password)) {
            throw new Error('Can\'t authenticate');
        }
    });
}

回答1:


Yes, you can always flatten a promise chain with Promise.all (shorthand through Promise.join in Bluebird) by using promises for the proxies they are. After all - promises abstract values, you can always unwrap a promise as late as you want and have other variables depend on it.

Whether or not it's more readable is another debate:

foo().then(function(a){
     return bar(a).then(function(b){
          return g(a,b); // "needed" to nest because I wanted `a`
     });
});

Can be written as:

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

So generally - you can always avoid nesting but a lot of time you might not want to.

In your case, this can even be:

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("..."); });
}

Flatter? Sure. Better? That's a whole other question. If you have a nested for loop, you might want to keep it a nested for loop and nest rather than hack around that option and use a single loop.



来源:https://stackoverflow.com/questions/26204865/can-you-avoid-nesting-altogether-with-promises

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!