关于javascript:什么是显式承诺构造反模式,我该如何避免它?

What is the explicit promise construction antipattern and how do I avoid it?

我编写的代码看起来像:

1
2
3
4
5
6
7
8
9
10
11
function getStuffDone(param) {           | function getStuffDone(param) {
    var d = Q.defer(); /* or $q.defer */ |     return new Promise(function(resolve, reject) {
    // or = new $.Deferred() etc.        |     // using a promise constructor
    myPromiseFn(param+1)                 |         myPromiseFn(param+1)
    .then(function(val) { /* or .done */ |         .then(function(val) {
        d.resolve(val);                  |             resolve(val);
    }).catch(function(err) { /* .fail */ |         }).catch(function(err) {
        d.reject(err);                   |             reject(err);
    });                                  |         });
    return d.promise; /* or promise() */ |     });
}                                        | }

有人告诉我这个被称为"延迟反模式"或"Promise构造函数反模式",这个代码有什么不好,为什么这被称为反模式?


Esailija创造的延迟反模式(现在是明确构建的反模式)是一种常见的反模式人物,他们是新的承诺,我在我第一次使用承诺时自己创造了。上述代码的问题在于无法利用promises链的事实。

Promise可以与.then链接,你可以直接返回promises。您在getStuffDone中的代码可以重写为:

1
2
3
function getStuffDone(param){
    return myPromiseFn(param+1); // much nicer, right?
}

Promise都是为了使异步代码更具可读性,并且表现得像同步代码而不隐藏这一事实。 Promise表示对一次操作的值的抽象,它们在编程语言中抽象出语句或表达式的概念。

在将API转换为promises并且无法自动执行时,或者当您编写更容易表达的聚合函数时,您应该只使用延迟对象。

引用Esailija:

This is the most common anti-pattern. It is easy to fall into this when you don't really understand promises and think of them as glorified event emitters or callback utility. Let's recap: promises are about making asynchronous code retain most of the lost properties of synchronous code such as flat indentation and one exception channel.


它出什么问题了?

But the pattern works!

幸运的你。不幸的是,它可能没有,因为你可能忘记了一些边缘情况。在我看过的一半以上的事件中,作者忘记了处理错误处理程序:

1
2
3
4
5
return new Promise(function(resolve) {
    getOtherPromise().then(function(result) {
        resolve(result.property.example);
    });
})

如果另一个承诺被拒绝,这将被忽视,而不是传播到新的承诺(它将被处理) - 并且新承诺永远保持未决,这可能导致泄漏。

在你的回调代码导致错误的情况下会发生同样的事情 - 例如当result没有property并抛出异常时。那将是未经处理的,并且未能解决新的承诺。

相反,使用.then()会自动处理这两种情况,并在发生错误时拒绝新的承诺:

1
2
3
 return getOtherPromise().then(function(result) {
     return result.property.example;
 })

延迟反模式不仅麻烦,而且容易出错。使用.then()进行链接更加安全。

But I've handled everything!

真?好。但是,这将非常详细和丰富,特别是如果您使用支持其他功能(如取消或消息传递)的promise库。或者也许它将来,或者你想要将你的图书馆换成更好的图书馆?您不希望为此重写代码。

库的方法(then)不仅本身支持所有功能,它们也可能具有某些优化。使用它们可能会使您的代码更快,或者至少可以通过库的未来版本进行优化。

我该如何避免呢?

因此,每当您发现自己手动创建PromiseDeferred并且涉及现有的承诺时,请首先检查库API。延迟反模式通常由那些将promises [仅]视为观察者模式的人应用 - 但承诺不仅仅是回调:它们应该是可组合的。每个体面的图书馆都有许多易于使用的功能,以各种可想象的方式构成承诺,照顾你不想处理的所有低级别的东西。

如果您发现需要以现有辅助函数不支持的新方式编写某些promise,那么使用不可避免的Deferreds编写自己的函数应该是最后一个选项。考虑切换到功能更强大的库,和/或针对当前库提交错误。它的维护者应该能够从现有函数派生组合,为您实现一个新的辅助函数和/或帮助识别需要处理的边缘情况。