什么是显式promise构造反模式,如何避免呢?

本文翻译自:What is the explicit promise construction antipattern and how do I avoid it?

I was writing code that does something that looks like: 我正在编写代码,执行以下操作:

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() */ |     });
}                                        | }

Someone told me this is called the " deferred antipattern " or the " Promise constructor antipattern " respectively, what's bad about this code and why is this called an antipattern ? 有人告诉我这分别称为“ 延迟反模式 ”或“ Promise构造函数反模式 ”,这段代码有什么不好之处,为什么又将其称为反模式 ?


#1楼

参考:https://stackoom.com/question/1bsRL/什么是显式promise构造反模式-如何避免呢


#2楼

The deferred antipattern (now explicit-construction anti-pattern) coined by Esailija is a common anti-pattern people who are new to promises make, I've made it myself when I first used promises. 由Esailija创造的递延反模式(现在是显式构造反模式)是对诺言做出的新手的普通反模式人,当我第一次使用诺言时,我自己就做出了。 The problem with the above code is that is fails to utilize the fact that promises chain. 上面代码的问题是无法利用承诺链的事实。

Promises can chain with .then and you can return promises directly. 承诺可以与.then ,您可以直接返回承诺。 Your code in getStuffDone can be rewritten as: 您在getStuffDone代码可以重写为:

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

Promises are all about making asynchronous code more readable and behave like synchronous code without hiding that fact. 承诺都是关于使异步代码更具可读性,并且在不隐藏该事实的情况下像同步代码一样起作用。 Promises represent an abstraction over a value of one time operation, they abstract the notion of a statement or expression in a programming language. 承诺表示对一次操作值的抽象,它们抽象出一种编程语言中的语句或表达式的概念。

You should only use deferred objects when you are converting an API to promises and can't do it automatically, or when you're writing aggregation functions that are easier expressed this way. 仅在将API转换为Promise且无法自动执行时,或者在编写以这种方式表示的聚合函数时,才应使用延迟对象。

Quoting Esailija: 引用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. 让我们回顾一下:承诺是关于使异步代码保留同步代码的大部分丢失属性,例如平面缩进和一个异常通道。


#3楼

What's wrong with it? 它出什么问题了?

But the pattern works! 但是模式有效!

Lucky you. 幸运的你。 Unfortunately, it probably doesn't, as you likely forgot some edge case. 不幸的是,可能不会,因为您可能忘记了一些极端情况。 In more than half of the occurrences I've seen, the author has forgotten to take care of the error handler: 在我所见过的事件中,有一半以上是作者忘记照顾错误处理程序的:

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

If the other promise is rejected, this will happen unnoticed instead of being propagated to the new promise (where it would get handled) - and the new promise stays forever pending, which can induce leaks. 如果另一个承诺被拒绝,则将不会引起注意,而不是传播到新的承诺(将在该位置处理),并且新的承诺将永远挂起,从而导致泄漏。

The same thing happens in the case that your callback code causes an error - eg when result doesn't have a property and an exception is thrown. 在回调代码导致错误的情况下也会发生同样的事情-例如,当result没有property并且抛出异常时。 That would go unhandled and leave the new promise unresolved. 那将无法处理,并使新的承诺无法实现。

In contrast, using .then() does automatically take care of both these scenarios, and rejects the new promise when an error happens: 相反,使用.then()会自动处理这两种情况,并在发生错误时拒绝新的承诺:

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

The deferred antipattern is not only cumbersome, but also error-prone . 延迟的反模式不仅麻烦,而且容易出错 Using .then() for chaining is much safer. 使用.then()进行链接要安全得多。

But I've handled everything! 但是我已经处理了一切!

Really? 真? Good. 好。 However, this will be pretty detailed and copious, especially if you use a promise library that supports other features like cancellation or message passing. 但是,这将非常详细和丰富,特别是如果您使用支持其他功能(例如取消或消息传递)的Promise库。 Or maybe it will in the future, or you want to swap your library against a better one? 也许将来会,或者您想将图书馆换成更好的图书馆? You won't want to rewrite your code for that. 您将不想为此重写代码。

The libraries' methods ( then ) do not only natively support all the features, they also might have certain optimisations in place. 库的方法( then )不仅本地支持所有功能,而且还可能具有某些优化功能。 Using them will likely make your code faster, or at least allow to be optimised by future revisions of the library. 使用它们可能会使您的代码更快,或者至少允许通过该库的未来版本进行优化。

How do I avoid it? 如何避免呢?

So whenever you find yourself manually creating a Promise or Deferred and already existing promises are involved, check the library API first . 因此,每当您发现自己手动创建PromiseDeferred并且涉及已经存在的Promise请首先检查库API The Deferred antipattern is often applied by people who see promises [only] as an observer pattern - but promises are more than callbacks : they are supposed to be composable. 递延反模式经常被人谁看到诺言应用[唯一]作为一个观察者模式-但承诺都超过回调 :他们应该是组合的。 Every decent library has lots of easy-to-use functions for the composition of promises in every thinkable manner, taking care of all the low-level stuff you don't want to deal with. 每个体面的图书馆都有许多易于使用的功能,以各种可想而知的方式来构成承诺,可以处理您不想处理的所有低级内容。

If you have found a need to compose some promises in a new way that is not supported by an existing helper function, writing your own function with unavoidable Deferreds should be your last option. 如果您发现有需要以现有助手功能不支持的新方式撰写一些承诺,那么最后一个选择就是使用不可避免的Deferreds编写自己的函数。 Consider switching to a more featureful library, and/or file a bug against your current library. 考虑切换到功能更强大的库,和/或针对当前库提出错误。 Its maintainer should be able to derive the composition from existing functions, implement a new helper function for you and/or help to identify the edge cases that need to be handled. 它的维护者应该能够从现有功能中获得合成,为您实现新的帮助器功能和/或帮助确定需要处理的极端情况。

你可能感兴趣的:(javascript,promise,q,bluebird,es6-promise)