摘要:為的項,取出來的分別為和,所以上的和方法,調(diào)用的是中的方法,實質(zhì)是往各自的回調(diào)列表中添加回調(diào)函數(shù)。進度回調(diào)函數(shù)數(shù)組。參數(shù)為異步對象的索引值,參數(shù)為對應(yīng)的上下文數(shù)組,即或,為對應(yīng)的回調(diào)函數(shù)數(shù)組,即或。
Deferred 模塊也不是必備的模塊,但是 ajax 模塊中,要用到 promise 風格,必需引入 Deferred 模塊。Deferred 也用到了上一篇文章《讀Zepto源碼之Callbacks模塊 )》介紹的 Callbacks 模塊。
讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto
源碼版本本文閱讀的源碼為 zepto1.2.0
Promise/A+ 規(guī)范規(guī)范的具體內(nèi)容可以參考《Promises/A+》 和對應(yīng)的中文翻譯 《Promise/A+規(guī)范》,這里只簡單總結(jié)一下。
promise 是一個包含兼容 promise 規(guī)范的函數(shù)或?qū)ο螅?b>promise 包含三種狀態(tài) pending 進行中、fulfilled 已完成, rejected 被拒絕,并且必須處于其中一種狀態(tài)。
pending 狀態(tài)可以轉(zhuǎn)換成 fulfilled 狀態(tài)或者 rejected 狀態(tài),但是 fulfilled 狀態(tài)和 rejected 狀態(tài)不能再轉(zhuǎn)換成其他狀態(tài)。
promise 必須包含 then 方法,then 方法可以接收兩個參數(shù),參數(shù)類型都為函數(shù),分別為狀態(tài)變?yōu)?fulfilled 后調(diào)用的 onFulfilled 函數(shù)和 rejected 后調(diào)用的 onRejected 函數(shù)。
大致了解 Promise/A+ 規(guī)范后,對后面源碼的閱讀會有幫助。
Deferred 模塊的整體結(jié)構(gòu);(function($){ function Deferred(func) { deferred = {} if (func) func.call(deferred, deferred) return deferred } return $.Deferred = Deferred })(Zepto)
從上面的精簡的結(jié)構(gòu)可以看出,Deferred 是一個函數(shù),函數(shù)的返回值是一個符合 Promise/A+ 規(guī)范的對象,如果 Deferred 有傳遞函數(shù)作為參數(shù),則以 deferred 作為上下文,以 deferred 作為參數(shù)執(zhí)行該函數(shù)。
done、fail、progress、resolve/resolveWith、reject/rejectWith、notify/notifyWith 方法的生成var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", $.Callbacks({once:1, memory:1}), "resolved" ], [ "reject", "fail", $.Callbacks({once:1, memory:1}), "rejected" ], [ "notify", "progress", $.Callbacks({memory:1}) ] ], state = "pending", promise = { ... } deferred = {} $.each(tuples, function(i, tuple){ var list = tuple[2], stateString = tuple[3] promise[tuple[1]] = list.add if (stateString) { list.add(function(){ state = stateString }, tuples[i^1][2].disable, tuples[2][2].lock) } deferred[tuple[0]] = function(){ deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments) return this } deferred[tuple[0] + "With"] = list.fireWith })變量解釋
tuples: 用來儲存狀態(tài)切換的方法名,對應(yīng)狀態(tài)的執(zhí)行方法,回調(diào)關(guān)系列表和最終的狀態(tài)描述。
state: 狀態(tài)描述
promise:promise 包含執(zhí)行方法 always 、then 、done、 fail 、progress 和輔助方法 state 、 promise 等
deferred: deferred 除了繼承 promise 的方法外,還增加了切換方法, resolve 、resoveWith 、reject 、 rejectWith 、notify 、 notifyWith 。
?
$.each(tuples, function(i, tuple){ ... })
方法的生成,通過遍歷 tuples 實現(xiàn)
var list = tuple[2], stateString = tuple[3] promise[tuple[1]] = list.add
list 是工廠方法 $.Callbacks 生成的管理回調(diào)函數(shù)的一系列方法。具體參見上一篇文章《讀Zepto源碼之Callbacks模塊》。注意,tuples 的所有項中的 $Callbacks 都配置了 memory:1 ,即開啟記憶模式,增加的方法都會立即觸發(fā)。包含 resove 和 reject 的項都傳遞了 once:1 ,即回調(diào)列表只能觸發(fā)一次。
stateString 是狀態(tài)描述,只有包含了 resolve 和 reject 的數(shù)組項才具有。
index 為 1 的項,取出來的分別為 done 、 fail 和 progress ,所以 promise 上的 done、 fail 和 progress 方法,調(diào)用的是 Callbacks 中的 add 方法,實質(zhì)是往各自的回調(diào)列表中添加回調(diào)函數(shù)。
狀態(tài)切換if (stateString) { list.add(function(){ state = stateString }, tuples[i^1][2].disable, tuples[2][2].lock) }
如果 stateString 存在,即包含 resolve 和 reject 的數(shù)組項,則往對應(yīng)的回調(diào)列表中添加切換 state 狀態(tài)的方法,將 state 更改為對應(yīng)方法觸發(fā)后的狀態(tài)。
同時,將狀態(tài)鎖定,即狀態(tài)變?yōu)?resolved 或 rejected 狀態(tài)后,不能再更改為其他狀態(tài)。這里用來按位異或運算符 ^ 來實現(xiàn)。當 i 為 0 ,即狀態(tài)變?yōu)?resolved 時, i^1 為 1 。tuples[i^1][2].disable 將 rejected 的回調(diào)列表禁用,當 i 為 1 時, i^1 為 0 ,將 resolved 的回調(diào)列表禁用。即實現(xiàn)了成功和失敗的狀態(tài)互斥,做得狀態(tài)鎖定,不能再更改。
在狀態(tài)變更后,同時將 tuples[2] 的回調(diào)列表鎖定,要注意 disable 和 lock 的區(qū)別,具體見《讀Zepto源碼之Callbacks模塊》,由于這里用了記憶模式,所以還可以往回調(diào)列表中添加回調(diào)方法,并且回調(diào)方法會立即觸發(fā)。
resolve/resolveWith、reject/rejectWith、notify/notifyWith 方法的生成deferred[tuple[0] + "With"] = list.fireWith deferred[tuple[0]] = function(){ deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments) return this }
這幾個方法,存入在 deferred 對象中,并沒有存入 promise 對象。
resolveWith 、 rejectWith 和 notifyWith 方法,其實等價于 Callback 的 fireWith 方法,fireWith 方法的第一個參數(shù)為上下文對象。
從源碼中可以看到 resolve 、reject 和 notify 方法,調(diào)用的是對應(yīng)的 With 后綴方法,如果當前上下文為 deferred 對象,則傳入 promise 對象作為上下文。
promise 對象 .state()state: function() { return state },
state 方法的作用是返回當前的狀態(tài)。
.always()always: function() { deferred.done(arguments).fail(arguments) return this },
always 是一種省事的寫法,即無論成功還是失敗,都會執(zhí)行回調(diào)。調(diào)用的是 deferred 上的 done 和 fail 方法。或許你會有疑惑,done 和 fail 方法,上面的分析中,明明是 promise 的方法,為什么 deferred 對象上也有這兩個方法呢,這個下面會講到。
.promise()promise: function(obj) { return obj != null ? $.extend( obj, promise ) : promise }
返回 promise 對象,如果 obj 有傳遞,則將 promise 上的方法擴展到 obj 上。
.then()then: function(/* fnDone [, fnFailed [, fnProgress]] */) { var fns = arguments return Deferred(function(defer){ $.each(tuples, function(i, tuple){ var fn = $.isFunction(fns[i]) && fns[i] deferred[tuple[1]](function(){ var returned = fn && fn.apply(this, arguments) if (returned && $.isFunction(returned.promise)) { returned.promise() .done(defer.resolve) .fail(defer.reject) .progress(defer.notify) } else { var context = this === promise ? defer.promise() : this, values = fn ? [returned] : arguments defer[tuple[0] + "With"](context, values) } }) }) fns = null }).promise() }
promise 的 then 方法接收三個參數(shù),分別為成功的回調(diào)、失敗的回調(diào)和進度的回調(diào)。
then整體結(jié)構(gòu)將 then 簡化后,可以看到以下的結(jié)構(gòu):
return Deferred(function(defer){}).promise()
返回的是 deferred 對象,deferred 對象上的 promise 方法,其實就是 promise 對象上的 promise 方法,所以 then 方法,最終返回的還是 promise 對象。所以 promise 可以這樣一直調(diào)用下去 promise().then().then().... 。
Deferred 調(diào)用var fns = arguments return Deferred(function(defer) { ... }) fns = null
這里的變量 fns 是 then 所傳入的參數(shù),即上文提到的三個回調(diào)。
最后的 fns = null ,是釋放引用,讓 JS 引擎可以進行垃圾回收。
Deferred 的參數(shù)是一個函數(shù),上文在分析總體結(jié)構(gòu)的時候,有一句關(guān)鍵的代碼 if (func) func.call(deferred, deferred) 。所以這里的函數(shù)的參數(shù) defer 即為 deferred 對象。
執(zhí)行回調(diào)$.each(tuples, function(i, tuple){ var fn = $.isFunction(fns[i]) && fns[i] deferred[tuple[1]](function(){ var returned = fn && fn.apply(this, arguments) if (returned && $.isFunction(returned.promise)) { returned.promise() .done(defer.resolve) .fail(defer.reject) .progress(defer.notify) } else { var context = this === promise ? defer.promise() : this, values = fn ? [returned] : arguments defer[tuple[0] + "With"](context, values) } }) })
遍歷 tuples , tuples 中的順序,跟 then 中規(guī)定 done 、fail 和 progress 的回調(diào)順序一致。
所以用 var fn = $.isFunction(fns[i]) && fns[i] 來判斷對應(yīng)位置的參數(shù)是否為 function 類型,如果是,則賦值給 fn 。
deferred[tuple[1]] 是對應(yīng)的是 done、fail 和 progress 。所以在 then 里,會依次執(zhí)行這三個方法。
var returned = fn && fn.apply(this, arguments)
returned 是 then 中三個回調(diào)方法執(zhí)行后返回的結(jié)果。
if (returned && $.isFunction(returned.promise)) { returned.promise() .done(defer.resolve) .fail(defer.reject) .progress(defer.notify) }
如果回調(diào)返回的是 promise 對象,調(diào)用新 promise 對象中的 promise 方法,新 promise 對象切換狀態(tài)時, 并將當前 deferred 對象對應(yīng)的狀態(tài)切換方法傳入,在新 promise 切換狀態(tài)時執(zhí)行。這就實現(xiàn)了兩個 promise 對象的狀態(tài)交流。
var context = this === promise ? defer.promise() : this, values = fn ? [returned] : arguments defer[tuple[0] + "With"](context, values)
如果返回的不是 promise 對象,則判斷 this 是否為 promise ,如果是,則返回 defer.promise() ,修正執(zhí)行的上下文。
然后調(diào)用對應(yīng)的狀態(tài)切換方法切換狀態(tài)。
promise 對象與 deferred 對象promise.promise(deferred)
從上面的分析中,可以看到,deferred 對象上并沒有done 、 fail 和 progress 方法,這是從 promise 上擴展來的。
既然已經(jīng)有了一個擁有 promise 對象的所有方法的 deferred 對象,為什么還要一個額外的 promise 對象呢?
promise 對象上沒有狀態(tài)切換方法,所以在 then 中,要綁定上下文的時候時候,綁定的都是 promise 對象,這是為了避免在執(zhí)行的過程中,將執(zhí)行狀態(tài)改變。
$.when$.when = function(sub) { var resolveValues = slice.call(arguments), len = resolveValues.length, i = 0, remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0, deferred = remain === 1 ? sub : Deferred(), progressValues, progressContexts, resolveContexts, updateFn = function(i, ctx, val){ return function(value){ ctx[i] = this val[i] = arguments.length > 1 ? slice.call(arguments) : value if (val === progressValues) { deferred.notifyWith(ctx, val) } else if (!(--remain)) { deferred.resolveWith(ctx, val) } } } if (len > 1) { progressValues = new Array(len) progressContexts = new Array(len) resolveContexts = new Array(len) for ( ; i < len; ++i ) { if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) { resolveValues[i].promise() .done(updateFn(i, resolveContexts, resolveValues)) .fail(deferred.reject) .progress(updateFn(i, progressContexts, progressValues)) } else { --remain } } } if (!remain) deferred.resolveWith(resolveContexts, resolveValues) return deferred.promise() }
when 方法用來管理一系列的異步隊列,如果所有的異步隊列都執(zhí)行成功,則執(zhí)行成功方法,如果有一個異步執(zhí)行失敗,則執(zhí)行失敗方法。這個方法也可以傳入非異步方法。
一些變量var resolveValues = slice.call(arguments), len = resolveValues.length, i = 0, remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0, deferred = remain === 1 ? sub : Deferred(), progressValues, progressContexts, resolveContexts,
resolveValues:所有的異步對象,用 slice 轉(zhuǎn)換成數(shù)組形式。
len: 異步對象的個數(shù)。
remain: 剩余個數(shù)。這里還有個判斷,是為了確定只有一個參數(shù)時,這個參數(shù)是不是異步對象,如果不是,則 remain 初始化為 0 。其他情況,初始化為當前的個數(shù)。
i: 當前異步對象執(zhí)行的索引值。
deferred: deferred 對象,如果只有一個異步對象(只有一個參數(shù),并且不為異步對象時, remain 為 0 ),則直接使用當前的 deferred 對象,否則創(chuàng)建一個新的 deferred 對象。
progressValues: 進度回調(diào)函數(shù)數(shù)組。
progressContexts: 進度回調(diào)函數(shù)綁定的上下文數(shù)組
resolveContexts: 成功回調(diào)函數(shù)綁定的上下文數(shù)組
updateFnupdateFn = function(i, ctx, val){ return function(value){ ctx[i] = this val[i] = arguments.length > 1 ? slice.call(arguments) : value if (val === progressValues) { deferred.notifyWith(ctx, val) } else if (!(--remain)) { deferred.resolveWith(ctx, val) } } }
updateFn 方法,在每個異步對象執(zhí)行 resolve 方法和 progress 方法時都調(diào)用。
參數(shù) i 為異步對象的索引值,參數(shù) ctx 為對應(yīng)的上下文數(shù)組,即 resolveContexts 或 resolveContexts , val 為對應(yīng)的回調(diào)函數(shù)數(shù)組,即 progresValues 或 resolveValues 。
if (val === progressValues) { deferred.notifyWith(ctx, val) }
如果為 progress 的回調(diào),則調(diào)用 deferred 的 notifyWith 方法。
else if (!(--remain)) { deferred.resolveWith(ctx, val) }
否則,將 remain 減少 1,如果回調(diào)已經(jīng)執(zhí)行完畢,則調(diào)用 deferred 的 resolveWith 方法。
依次處理異步對象if (len > 1) { progressValues = new Array(len) progressContexts = new Array(len) resolveContexts = new Array(len) for ( ; i < len; ++i ) { if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) { resolveValues[i].promise() .done(updateFn(i, resolveContexts, resolveValues)) .fail(deferred.reject) .progress(updateFn(i, progressContexts, progressValues)) } else { --remain } } }
首先初始化 progressValues 、progressContexts 和 resolveContexts ,數(shù)組長度為異步對象的長度。
if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) { resolveValues[i].promise() .done(updateFn(i, resolveContexts, resolveValues)) .fail(deferred.reject) .progress(updateFn(i, progressContexts, progressValues)) }
如果為 promise 對象,則調(diào)用對應(yīng)的 promise 方法。
else { --remain }
如果不是 promise 對象,則將 remian 減少 1 。
if (!remain) deferred.resolveWith(resolveContexts, resolveValues) return deferred.promise()
如果無參數(shù),或者參數(shù)不是異步對象,或者所有的參數(shù)列表都不是異步對象,則直接調(diào)用 resoveWith 方法,調(diào)用成功函數(shù)列表。
最后返回的是 promise 對象。
系列文章讀Zepto源碼之代碼結(jié)構(gòu)
讀 Zepto 源碼之內(nèi)部方法
讀Zepto源碼之工具函數(shù)
讀Zepto源碼之神奇的$
讀Zepto源碼之集合操作
讀Zepto源碼之集合元素查找
讀Zepto源碼之操作DOM
讀Zepto源碼之樣式操作
讀Zepto源碼之屬性操作
讀Zepto源碼之Event模塊
讀Zepto源碼之IE模塊
讀Zepto源碼之Callbacks模塊
參考Zepto源碼分析-deferred模塊
Promises/A+
Promise/A+規(guī)范
jQuery的deferred對象詳解
License最后,所有文章都會同步發(fā)送到微信公眾號上,歡迎關(guān)注,歡迎提意見:
作者:對角另一面
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/84481.html
摘要:私有變量用來臨時存放配置中的,即請求成功后執(zhí)行的回調(diào)函數(shù)名,該配置可以為類型。是根據(jù)配置得出的回調(diào)函數(shù)名。接下來,將的占位符,替換成回調(diào)函數(shù)名,最后將插入到頁面中,發(fā)送請求。 Ajax 模塊也是經(jīng)常會用到的模塊,Ajax 模塊中包含了 jsonp 的現(xiàn)實,和 XMLHttpRequest 的封裝。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-...
摘要:讀源碼系列文章已經(jīng)放到了上,歡迎源碼版本本文閱讀的源碼為改寫原有的方法模塊改寫了以上這些方法,這些方法在調(diào)用的時候,會為返回的結(jié)果添加的屬性,用來保存原來的集合。方法的分析可以看讀源碼之模塊。 Stack 模塊為 Zepto 添加了 addSelf 和 end 方法。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto 源碼版本 本文閱讀的...
摘要:模塊是為解決移動版加載圖片過大過多時崩潰的問題。因為沒有處理過這樣的場景,所以這部分的代碼解釋不會太多,為了說明這個問題,我翻譯了這篇文章作為附文怎樣處理移動端對圖片資源的限制,更詳細地解釋了這個模塊的應(yīng)用場景。 assets 模塊是為解決 Safari 移動版加載圖片過大過多時崩潰的問題。因為沒有處理過這樣的場景,所以這部分的代碼解釋不會太多,為了說明這個問題,我翻譯了《How to...
摘要:模塊基于上的事件的封裝,利用屬性,封裝出系列事件。這個判斷需要引入設(shè)備偵測模塊。然后是監(jiān)測事件,根據(jù)這三個事件,可以組合出和事件。其中變量對象和模塊中的對象的作用差不多,可以先看看讀源碼之模塊對模塊的分析。 Gesture 模塊基于 IOS 上的 Gesture 事件的封裝,利用 scale 屬性,封裝出 pinch 系列事件。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡...
摘要:模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數(shù)據(jù),另一部分是觸發(fā)事件,提交表單。最終返回的結(jié)果是一個數(shù)組,每個數(shù)組項為包含和屬性的對象。否則手動綁定事件,如果沒有阻止瀏覽器的默認事件,則在第一個表單上觸發(fā),提交表單。 Form 模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數(shù)據(jù),另一部分是觸發(fā) submit 事件,提交表單。 讀 Zepto 源碼系列文章已...
閱讀 1273·2021-11-24 09:39
閱讀 1526·2021-09-07 09:59
閱讀 3484·2019-08-30 15:54
閱讀 2480·2019-08-30 11:00
閱讀 2675·2019-08-29 15:06
閱讀 2165·2019-08-26 13:52
閱讀 436·2019-08-26 13:24
閱讀 2501·2019-08-26 12:20