什麼是顯式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. 它的維護者應該能夠從現有功能中獲得合成,爲您實現新的幫助器功能和/或幫助確定需要處理的極端情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章