你能避免使用 Promises 完全嵌套吗? [复制]

Posted

技术标签:

【中文标题】你能避免使用 Promises 完全嵌套吗? [复制]【英文标题】:Can you avoid nesting altogether with Promises? [duplicate] 【发布时间】:2014-11-30 01:00:19 【问题描述】:

据我了解,Promises 的主要卖点之一是能够编写扁平代码(或者,比回调地狱更扁平)。

虽然看起来在很多情况下我们需要嵌套 Promise 才能使用闭包。例如(来自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');
        
    );

【问题讨论】:

干净一点:jsfiddle.net/jnqx4uqw 是的,但它并没有摆脱嵌套 有时 promise 不需要嵌套,有时则不需要。这真的取决于情况的细节。如果您正在对操作进行排序,则不必仅仅为了共享结果而嵌套。您可以将值分配给公共父闭包中可用的东西,也可以这样做,然后不必嵌套。 没有。消除所谓的回调地狱只是使用 Promises 的原因之一,IMO 不太重要。如果您考虑一下,回调仍然存在,它们只是包装在一些承诺库中。 【参考方案1】:

是的,您始终可以使用代理的 Promise 来扁平化带有 Promise.all 的 Promise 链(在 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 循环并嵌套,而不是绕过该选项并使用单个循环。

【讨论】:

好东西,您的 a、b、c 示例正是我想要的。附带说明一下,我不喜欢使用 .join,因为即使它节省了几次击键,我觉得它的可读性较差(而且我很确定它不在标准中) 没关系,我只是习惯了,因为这种模式非常常见,它为我节省了.spread,而且速度也快了一点。

以上是关于你能避免使用 Promises 完全嵌套吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

你能“累积”承诺结果形成一连串“那么”吗? [复制]

你能嵌套html表单吗?

你能嵌套html表单吗?

Android:你能嵌套 Fragments 吗?

使用嵌套命令时可以避免打开 DataReader 异常吗?

避免使用promises中的内存泄漏并在咖啡脚本中循环(无需等待)