什么是显性承诺建设反例,如何避免?

javascript promise q bluebird es6-promise


我在写代码,做一些看起来像这样的事情。

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 构造函数反模式 ”,这段代码有什么不好之处,为什么又将其称为反模式 ?





Answer 1 Benjamin Gruenbaum


Esailija创造的递延反模式(现在是显式构造反模式)是一个新的对诺言做出新承诺的普通反模式人,当我第一次使用诺言时,我自己就做出了。 上面代码的问题是无法利用承诺链的事实。

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

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

承诺的作用就是让异步代码更易读,表现得像同步代码一样,但又不掩盖这个事实。承诺代表了对一个时间操作的值的抽象,它们抽象了编程语言中的语句或表达式的概念。

仅在将API转换为Promise且无法自动执行时,或者在编写以这种方式表示的聚合函数时,才应使用延迟对象。

引述Esailija的话:

这是最常见的反模式。当你不真正理解承诺的时候,很容易落入其中,把它们当成是美化的事件发射器或回调实用程序。让我们回顾一下:承诺就是让异步代码保留了同步代码的大部分丢失的属性,比如扁平化的缩进和一个异常通道。




Answer 2 Bergi


这有什么问题吗?

但这个模式很有效!

你很幸运。不幸的是,它可能没有,因为你很可能忘记了一些边缘情况。在我见过的超过一半的情况下,作者忘记了对错误处理程序的照顾。

return new Promise(function(resolve) {
    getOtherPromise().then(function(result) {
        resolve(result.property.example);
    });
})

如果另一个承诺被拒绝,这种情况会在不被注意到的情况下发生,而不是传播到新的承诺(在那里会得到处理)--而新的承诺会永远停留在悬而未决的状态,这可能会诱发泄密。

在回调代码导致错误的情况下也会发生相同的事情-例如,当 result 没有 property 并且引发异常时。 那将无法处理,并使新的承诺无法实现。

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

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

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

但我已经处理好了一切!

真的吗?很好。然而,这将是相当详细和繁琐的,尤其是如果你使用的是支持其他功能的承诺库,比如取消或消息传递等功能的承诺库。或者说,也许它将来会支持,或者你想用更好的库来交换你的库?你不会想为此重写你的代码。

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

如何避免?

因此,每当您发现自己手动创建 PromiseDeferred 并且涉及已经存在的Promise时 , 请首先检查库API 。 Deferred反模式通常由将promise(仅)视为观察者模式的人应用,但是promise不仅仅是回调 :它们应该是可组合的。 每个体面的图书馆都有许多易于使用的功能,以各种可想而知的方式来构成承诺,可以处理所有您不想处理的低级内容。

如果你发现需要用一种新的方式来编写一些承诺,而现有的帮助函数不支持的话,那么编写自己的函数,加上不可避免的Deferreds应该是你最后的选择。考虑切换到一个功能更强大的库,或者对你当前的库提出bug。它的维护者应该能够从现有的函数中推导出组成,为你实现一个新的辅助函数,或者帮助你识别需要处理的边缘情况。