在分析Deferred之前我覺得還是有必要把老套的設計模式給搬出來,便于了解源碼!
<a></a>
觀察者模式( 又叫釋出者-訂閱者模式 )應該是最常用的模式之一.
它定義了一種一對多的關系讓多個觀察者對象同時監聽某一個主題對象,這個主題對象的狀态發生變化時就會通知所有的觀察者對象,使得它們能夠自動更新自己。
使用觀察者模式的好處:
支援簡單的廣播通信,自動通知所有已經訂閱過的對象。
頁面載入後目标對象很容易與觀察者存在一種動态關聯,增加了靈活性。
目标對象與觀察者之間的抽象耦合關系能夠單獨擴充以及重用。
在這種模式中,有兩類對象,分别是“觀察者-Observer”和“目标對象-Subject”。
目标對象中儲存着一份觀察者的清單,當目标對象的狀态發生改變的時候就主動向觀察者發出通知(調用觀察者提供的方法),進而建立一種釋出/訂閱的關系。
這一種釋出/訂閱的關系常用于實作事件、消息的處理系統。
Subject(抽象主題)
能夠知道它自己的觀察者,若幹觀察者對象可能監視一個主題對象;
提供一個接口,用來附加和取消觀察者對象;
Observer(抽象觀察者)
它為對象定義了一個(自我)更新的接口,并且當主題對象發生改變的時候能夠被通知;
ConcreteSubject(具體主題)
存儲具體觀察者感興趣的狀态;
當它的狀态改變的時候,通知它所有的觀察者對象;
ConcreteObserver(具體觀察者)
持有一個具體主題的引用;
存儲和主題對象一緻的狀态,并且該能夠保留該狀态;
實作抽象觀察者的Update接口,以便能夠同主題對象的狀态保持一緻;
當某個具體主題狀态發生改變的時候,通知它的所有觀察者對象,以便保證這些觀察者對象的狀态同它的狀态保持一緻;
當被告知具體主題對象發生改變,一個具體觀察者對象可能會去查詢主題對象,以便獲得一個消息;該觀察者使用這個消息來使它的狀态同主題對象的狀态保持一緻;
觀察者對象初始化變化請求,它不會立即更新,直到通過主題對象的Notify方法來獲得一個變化的通知。主題的Notify方法并非總是被主題對象調用,它也能夠被觀察者對象調用,或者完全被其他不同類型的對象調用。
可以建立一個Deferred對象直接,但你通常請求從一個資料源
資料源是一個函數,傳回一個延遲對象
延遲對象提供了通路資料源的資料,允許你把成功回調函數/或失敗函數其回調鍊
當資料源有結果,它将在延時對象上調用resolve(result)方法或者在失敗的情況下調用reject(failure)方法,這導緻延遲對象的回調鍊被釋放了——這意味着每個連結鍊(回調或errback)被稱為反過來,結果是輸入到第一個回調,它的輸出是輸入到下一個回調(等等)
如果一個callback (or errback) 傳回一個 Failure對象,那麼下一個就是errback調用,是否就是callback調用
Deferred的定義:
由于Promises對于新手而言了解曲線還是比較陡峭的,這裡循序漸進的給大家介紹,同時實作一個最簡單的Promises/A代碼
Promises/A有個别名叫做“thenable”,就是“可以then”的。這裡一個promise有三種狀态:[預設、完成、失敗],初始建立的時候是預設狀态,狀态隻可以從預設變成完成,或者預設變成失敗。一旦完成或者失敗,狀态就不能再變定義的接口
Deferred提供的API
2
3
4
5
6
7
8
9
10
<code>var</code> <code>DeferredAPI = {</code>
<code> </code><code>deferred : deferred,</code>
<code> </code><code>all : all,</code>
<code> </code><code>Deferred : Deferred,</code>
<code> </code><code>DeferredList : DeferredList,</code>
<code> </code><code>wrapResult : wrapResult,</code>
<code> </code><code>wrapFailure : wrapFailure,</code>
<code> </code><code>Failure : Failure</code>
<code>}</code>
我們通過簡單的demo來解析程式的執行流程
11
12
<code>function</code> <code>asynchronous(delay,name) {</code>
<code> </code><code>var</code> <code>d =</code><code>new</code> <code>deferred.Deferred()</code>
<code> </code><code>setTimeout(</code><code>function</code><code>() {</code>
<code> </code><code>d.resolve(</code><code>'執行'</code><code>+name)</code>
<code> </code><code>}, delay || 1000);</code>
<code> </code><code>return</code> <code>d</code>
<code>};</code>
<code>var</code> <code>d1 =</code><code>new</code> <code>asynchronous(1000,</code><code>'d1'</code><code>);</code>
<code>d1.then(</code><code>function</code><code>(result){</code>
<code> </code><code>alert(result)</code><code>//結果是 執行d1</code>
<code>})</code>
執行asynchronous方法,傳入參數
new deferred.Deferred() 用來構造一個Deferred對象
setTimeout 模拟異步執行操作
d.resolved表示該操作成功完成了
傳回Deferred對象
then 增加成功回調函數和失敗回調函數到各自的隊列中便捷方法,兩個參數可以是數組或null
setTimeout 執行完畢後出發resolved方法
執行then注冊的回調函數
執行結束
通過檢視源碼就能發現,其實Deferred.js的官方API不清晰,内部還有很多接口的實作沒有說明
内部實作的處理器就2個子產品,分别是:
處理單個異步 Deferred 類
處理多個異步 DeferredList 類
二着是等同的,最終的實作是Deferred類,每次執行個體化一次就是一個新是上下文
Deferred在構造之後,相對的執行個體就具有了以下方法:
cancel
then
fail
both
resolve
reject
pause
unpause
inspect
thenReturn
thenCall
failReturn
failCall
可以直接傳入二個回調函數,分别對應都 done|fail 二個狀态的回調, 跟 $.jquery還是有差別,可以直接傳入三個回調函數,分别對應done|fail|progress三個狀态的回調
用this.callbakcs 存儲具體觀察者感興趣的内容
代碼中間有很多邏輯判斷,我們暫且先跳過,看看最終的執行方法
View Code
其中d就是傳入的異步對象了
d.callbacks就是訂閱收集的具體觀察者感興趣的内容,也就是callback or errback
比對出正确的回調方法,執行
d.result = fn(d.result) 傳入回調函數需要的參數
單一的流程就執行完畢了
我們看看複雜點的構造
等待許多異步資料源
13
14
15
<code>var</code> <code>d1 =</code><code>new</code> <code>asynchronous(2000,</code><code>'d1'</code><code>);</code>
<code>var</code> <code>d2 =</code><code>new</code> <code>asynchronous(3000,</code><code>'d2'</code><code>);</code>
<code>deferred.all([d1, d2]).then(</code><code>function</code><code>(result){</code>
<code> </code><code>console.log(result) </code><code>//["執行d1", "執行d2"]</code>
<code>}).thenCall(console.log(11111));</code>
執行流程:
等待在一個清單的所有值的遞延的對象,或者開始一群延遲操作和運作回調鍊當第一個成功還是失敗了
等待d1, d2都加載完畢後接受到所有的values後,deferred.all運作作用域鍊,當然也一樣可以提供成功或者失敗
傳回的result将會是數組格式的 <code>convert ['a', 'b', 'c'] to 'abc'</code>
或者傳入配置參數:
fireOnFirstResult: true 隻要傳回第一個d1處理
fireOnFirstError: true 隻傳回第一個錯誤的處理
<code>deferred.all([d1, d2],{fireOnFirstResult:</code><code>true</code><code>}).then(</code><code>function</code><code>(result){</code>
<code> </code><code>console.log(result) </code><code>//["執行d1"]</code>
最後看2個API沒有給出的接口
<code>dAll.failCall(console.error)</code>
<code>dAll.then(join).thenCall(console.log)</code>
任意組合模式
16
17
18
19
20
<code> </code><code>var</code> <code>d =</code><code>new</code> <code>deferred.Deferred()</code>
<code> </code><code>setTimeout(</code><code>function</code><code>() {</code>
<code> </code><code>d.resolve(</code><code>'執行'</code><code>+name)</code>
<code> </code><code>}, delay || 1000);</code>
<code> </code><code>return</code> <code>d</code>
<code> </code><code>};</code>
<code> </code><code>var</code> <code>d1 =</code><code>new</code> <code>asynchronous(2000,</code><code>'d1'</code><code>);</code>
<code>var</code> <code>d = d1.then(</code><code>function</code><code>(result1) {</code>
<code> </code><code>var</code> <code>d2 =</code><code>new</code> <code>asynchronous(200,</code><code>'d2'</code><code>);</code>
<code> </code><code>return</code> <code>d2.then(</code><code>function</code><code>(result2) {</code>
<code> </code><code>return</code> <code>result</code>
<code> </code><code>})</code>
<code>}).then(</code><code>function</code><code>(data) {</code>
<code> </code><code>console.log(data)</code>
<code> </code><code>return</code> <code>data</code>
一個回調函數的傳回值被傳遞到下一個回調。
當一個回調傳回一個延遲對象,原來的延遲将透明地等待其他接收它的值,然後運作它自己的回調鍊使用該值。我們把這種叫做嵌套。
總結:
現在我們可以用deferred對象做很多事了,傳回它,将它傳遞給另一個函數,等。
随後的回調注冊在deferred将收到傳回的值從最内層的回調:result1+ result2。
用起來是不是很爽!
本文轉自艾倫 Aaron部落格園部落格,原文連結:http://www.cnblogs.com/aaronjs/p/3169328.html,如需轉載請自行聯系原作者