屈屈的文章中詳細介紹了when.js,在這裡關于when.js的使用我就不多複述了,大家可以自己去研究它的api。
在這裡,我主要想讨論的是如何實作一個when.js類似的promise/a架構。為了更清晰了解實作原理,我略過when.js中一些比較強大的功能,隻實作其中最核心的功能,包括基本的then(),otherwise()以及比較好用的all()和any()。
下面看一下promise的基本資料結構:
我們可以看到,一個promise包含五個屬性,一個resolves數組用來存放當狀态轉換為fulfilled之時需要執行的動作,rejects數組用來存放當狀态轉換為rejected時需要執行的動作,一個readystate屬性用來存放目前的promise對象的狀态,一個data屬性用來存放調用resolve時傳遞參數,一個_reason屬性用來存放調用reject時傳遞的參數。
詳細的參數說明我們繼續看後面的實作會比較明白:
promise.prototype.then 是整個元件裡面最複雜的地方,代碼直接閱讀可能看起來會比較不明白,我後面會詳細講,在這裡先暫時把這個方法做一個簡化,便于大家了解其中最核心的内容:
簡化成這樣,看起來就簡單明了了吧,實際上就是當promise狀态為pending的時候,如果有執行then,需要将onfulfilled和onreject暫存起來,等到真正的異步操作執行完成後再觸發。那麼為什麼這樣簡單的寫法不行,需要上面那種複雜寫法呢?我們慢慢來往下看—— resolve和reject兩個方法就很簡單了,實際上就是看是否有暫存起來的操作需要執行,如果有的話,就把這些操作執行了。
這裡我用了和when.js一樣的思路,将resolve和reject定義在一個新的defer對象上,這樣是為了将這兩個方法封裝在使用promise的方法内部,避免使用者讓promise在外部操作狀态改變,進而增加程式複雜度。 有了這個defer之後,我們就可以很友善地将一個方法寫成promise了——
寫法上是不是跟when.js一樣?
但是簡化版的promise有個很重要的問題沒有解決——then的鍊式調用。因為如果沒有鍊式調用,就沒法解決異步嵌套的問題,那樣promise也就失去了存在的意義。
現在我們再回過頭來看看為什麼要寫複雜的then——
我們看一下類似于下面這種調用情況——
這段代碼在屈屈童鞋的那篇文章中出現,它最重要的是 getdata().then(getimg).then(showimg) 這種鍊式形式,表示先通過jsonp獲得image資料,然後再通過資料展現出圖檔,這種化異步嵌套為可讀性更好的鍊式調用形式正是promise規範存在的意義所在,那麼如何實作這一點呢?
仔細觀察可以發現,如果把前面兩級看作一個整體,(getdata().then(getimg)).then(showimg)顯然是一個單一的promise,這個promise我們可以通過一個範式來表達一下——
上面這個代碼是什麼意思呢?其實就是說,要實作a().then(b).then(c),其實等價于需要 a().then(b)傳回一個新的promise,而這個新的promise是相當于當then(b)中的b被調用的時候,執行resolve操作,是以用以下方法傳給a的resolve隊列替代原先的"b"方法即可——
想通了上面這一點,就好了解那個複雜的then了,正是做了這件事情,用下面的方法——
替代了直接push進onfulfilled到_resolves。
講到這裡,我想強調一下,promise規範的神奇之處就在這裡了——我們恰恰是用了promise規範本身實作了這個規範實作的最難之處——then的鍊式調用~
寫通了這個核心部分,那麼剩下的功能就不複雜了,我們既然可以用promise規範來實作promise本身的核心代碼,當然也可以用它來實作all和any等功能了,那些相對來說都會是非常簡單的問題——
從上面的代碼可以看到,all和any都可以通過promise本身輕松實作,其邏輯并不複雜。順便我們實作了qw.p.defer()這個文法糖。上面的代碼的例子是基于qwrap的,但是我們會發現将它獨立出來并不複雜,因為它隻是依賴于arrayh.foreach和objecth.mix,直接從qw中copy過來這兩個方法就好了。
最後我們看一下完整的代碼——
<code></code>