姊妹篇 移動web app開發必備 - 異步隊列 Deferredhtml
在分析Deferred以前我以爲仍是有必要把老套的設計模式給搬出來,便於理解源碼!jquery
觀察者模式( 又叫發佈者-訂閱者模式 )應該是最經常使用的模式之一.web
它定義了一種一對多的關係讓多個觀察者對象同時監聽某一個主題對象,這個主題對象的狀態發生變化時就會通知全部的觀察者對象,使得它們可以自動更新本身。編程
使用觀察者模式的好處:設計模式
觀察者對象初始化變化請求,它不會當即更新,直到經過主題對象的Notify方法來得到一個變化的通知。主題的Notify方法並不是老是被主題對象調用,它也可以被觀察者對象調用,或者徹底被其餘不一樣類型的對象調用。數組
Deferred的定義:promise
Deferred提供的APIapp
var DeferredAPI = { deferred : deferred, all : all, Deferred : Deferred, DeferredList : DeferredList, wrapResult : wrapResult, wrapFailure : wrapFailure, Failure : Failure }
咱們經過簡單的demo來解析程序的執行流程異步
function asynchronous(delay,name) { var d = new deferred.Deferred() setTimeout(function() { d.resolve('執行'+name) }, delay || 1000); return d }; var d1 = new asynchronous(1000, 'd1'); d1.then(function(result){ alert(result) //結果是 執行d1 })
經過查看源碼就能發現,其實Deferred.js的官方API不清晰,內部還有不少接口的實現沒有說明async
內部實現的處理器就2個模塊,分別是:
new deferred.Deferred() deferred.deferred()
二着是等同的,最終的實現是Deferred類,每次實例化一次就是一個新是上下文
Deferred在構造以後,相對的實例就具備瞭如下方法:
來,訂閱一個
Deferred.prototype.then = function (callback, errback) { this.callbacks.push({callback: callback, errback: errback}) if (this.called) _run(this) return this }
能夠直接傳入二個回調函數,分別對應都 done|fail 二個狀態的回調, 跟 $.jquery仍是有區別,能夠直接傳入三個回調函數,分別對應done|fail|progress三個狀態的回調
用this.callbakcs 存儲具體觀察者感興趣的內容
發佈通知
Deferred.prototype.resolve = function(result) { _startRun(this, result) return this }
代碼中間有不少邏輯判斷,咱們暫且先跳過,看看最終的執行方法
1 function _run(d) { 2 if (d.running) return 3 var link, status, fn 4 if (d.pauseCount > 0) return 5 6 while (d.callbacks.length > 0) { 7 link = d.callbacks.shift() 8 status = (d.result instanceof Failure) ? 'errback' : 'callback' 9 fn = link[status] 10 if (typeof fn !== 'function') continue 11 try { 12 d.running = true 13 d.result = fn(d.result) 14 d.running = false 15 if (d.result instanceof Deferred) { 16 d.pause() 17 _nest(d) 18 return 19 } 20 } catch (e) { 21 if (Deferred.consumeThrownExceptions) { 22 d.running = false 23 var f = new Failure(e) 24 f.source = f.source || status 25 d.result = f 26 if (d.verbose) { 27 console.warn('uncaught error in deferred ' + status + ': ' + e.message) 28 console.warn('Stack: ' + e.stack) 29 } 30 } else { 31 throw e 32 } 33 } 34 } 35 }
咱們看看複雜點的構造
等待許多異步數據源
function asynchronous(delay,name) { var d = new deferred.Deferred() setTimeout(function() { d.resolve('執行'+name) }, delay || 1000); return d }; var d1 = new asynchronous(2000, 'd1'); var d2 = new asynchronous(3000, 'd2'); deferred.all([d1, d2]).then(function(result){ console.log(result) //["執行d1", "執行d2"] }).thenCall(console.log(11111));
執行流程:
convert ['a', 'b', 'c'] to 'abc'
或者傳入配置參數:
fireOnFirstResult: true 只要返回第一個d1處理
fireOnFirstError: true 只返回第一個錯誤的處理
deferred.all([d1, d2],{fireOnFirstResult: true}).then(function(result){ console.log(result) //["執行d1"] }).thenCall(console.log(11111));
最後看2個API沒有給出的接口
dAll.failCall(console.error) dAll.then(join).thenCall(console.log)
任意組合模式
function asynchronous(delay,name) { var d = new deferred.Deferred() setTimeout(function() { d.resolve('執行'+name) }, delay || 1000); return d }; var d1 = new asynchronous(2000, 'd1'); var d = d1.then(function(result1) { var d2 = new asynchronous(200, 'd2'); return d2.then(function(result2) { return result }) }).then(function(data) { console.log(data) return data })
執行流程:
總結:
考慮這章拉的太長了,你們看的會有點小悶,因此下一節就開始隊列的源碼分析了