天天看點

JavaScript中的異步梳理(2)——使用Promises/A

Promises/A有個别名叫做“thenable”,就是“可以then”的。這裡一個promise有三種狀态:[預設、完成、失敗],初始建立的時候是預設狀态,狀态隻可以從預設變成完成,或者預設變成失敗。一旦完成或者失敗,狀态就不能再變。為了簡化文章,這裡我們先隻考慮完成,不考慮失敗。

構造函數中的ok是一個任務,promise.resolve(obj)表示将該promise的狀态改為完成,此時ok會被執行,其傳回值作為後續操作的參數以及resolve的傳回值。

由于沒有和任何異步操作關聯在一起,這裡的Promise還沒有任何作用。

Promises/A之是以叫“thenable”是因為它的核心API叫做then,望文生義這個方法的作用是當一個promise完成或失敗後繼續幹别的事情。

then傳入一個函數作為參數nextOK①,當該promise被resolve時,resolve的傳回值将會傳遞到nextOK中。

then傳回一個promise,當上述後續操作完成時,傳回的promise也會被resolve。

如果promise的狀态是已完成,則nextOK會被立即調用。

但是這樣并無法異步,是以這裡有一個特殊情況,就是如果nextOK的傳回值也是一個Promise,那麼then傳回的promise需要當這個promise被resolve時才會被resolve。

到到了這裡,我們的極簡版Promise就完成了,那麼如何使用呢?

這裡舉個例子,首先定義一些“任務”,例如:

按需要組織這些任務

可以看到控制台裡依次列印出了3、5和7。

但這些任務都是同步的,無法展現出Promise的強大之處——異步控制。這裡我們通過nextOK傳回promise的方法來實作一個delay。

利用它來改造上面的任務隊列,讓後兩次列印之間延遲2秒:

利用這個原理,可以做一些巧妙的代碼:

上面沒有使用循環,但是實作了一個無限每隔1秒自動列印的斐波那契數列。

Promises模型相當優雅,通過一些擴充可以實作諸如when, whenAll等API,對于封裝異步操作非常有幫助。

事實上的庫中不常直接用Promise這個名字,而常用Deferred,Defer的意思是“延遲”,是以Deferred常被成為“延遲隊列”或者“異步隊列”。在jQuery 1.5中引入了jQuery.Deferred,Dojo在這方面也是先行者,dojo 0.3就實作了Deferred。事實上在使用了Deferred之後,jQuery.ajax和dojo.ajax傳回的結果都是Deferred,是以可以用then取代傳統的傳入回調函數的形式,非常友善,例如在dojo中可以:

使用這樣的代碼可以随時對ajax請求添加回調,而不一定是在定義之初設定回調,靈活性更強。

而“設定一系列函數,在合适的時候調用它們;在此之後加入的函數将會被立即調用”這樣的特性簡直天生就和domReady是一對,實際上jQuery也使用Deferred重構了$.ready。

與此同時,借助Deferred實作動畫這樣的連續、并行的異步任務也非常優雅。

通過Promises模型,把異步操作都了解為異步“任務”,以任務為機關來組織排程異步操作,實際上已經有那麼點函數式的味道了。

下一篇文章,也是這個系列的最後一篇,将介紹另一種更加函數式的JavaScript異步操作組織方法。

①事實上Promises/A的定義要複雜的多,包括失敗reject等等,本文不細做闡述。

<b>本文轉自艾倫 Aaron部落格園部落格,原文連結:http://www.cnblogs.com/aaronjs/archive/2012/11/17/2774440.html,如需轉載請自行聯系原作者</b>

繼續閱讀