日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

最细的实现剖析:jQuery 2.0.3源码分析Deferred

發(fā)布時(shí)間:2025/3/20 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最细的实现剖析:jQuery 2.0.3源码分析Deferred 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Deferred的概念請(qǐng)看第一篇

http://www.cnblogs.com/aaronjs/p/3348569.html

**構(gòu)建Deferred對(duì)象時(shí)候的流程圖**

**源碼解析**

因?yàn)閏allback被剝離出去后,整個(gè)deferred就顯得非常的精簡(jiǎn)

jQuery.extend({
Deferred:function(){}

when:function()
)}
對(duì)于extend的繼承這個(gè)東東,在之前就提及過jquery如何處理內(nèi)部jquery與init相互引用this的問題

對(duì)于JQ的整體架構(gòu)一定要弄懂 http://www.cnblogs.com/aaronjs/p/3278578.html

所以當(dāng)jQuery.extend只有一個(gè)參數(shù)的時(shí)候,其實(shí)就是對(duì)jQuery靜態(tài)方法的一個(gè)擴(kuò)展

我們?cè)诰唧w看看2個(gè)靜態(tài)方法內(nèi)部都干了些什么:

Deferred整體結(jié)構(gòu):

源碼精簡(jiǎn)了部分代碼

Deferred:function(func){
vartuples=[
//action,addlistener,listenerlist,finalstate
["resolve","done",jQuery.Callbacks("oncememory"),"resolved"],
["reject","fail",jQuery.Callbacks("oncememory"),"rejected"],
["notify","progress",jQuery.Callbacks("memory")]
],
state="pending",
promise={
state:function(){},
always:function(){},
then:function(/fnDone,fnFail,fnProgress/){},
//Getapromiseforthisdeferred
//Ifobjisprovided,thepromiseaspectisaddedtotheobject
promise:function(obj){}
},
deferred={};
jQuery.each(tuples,function(i,tuple){
deferred[tuple[0]+"With"]=list.fireWith;
});
promise.promise(deferred);
//Alldone!
returndeferred;
},
顯而易見Deferred是個(gè)工廠類,返回的是內(nèi)部構(gòu)建的deferred對(duì)象
tuples 創(chuàng)建三個(gè)$.Callbacks對(duì)象,分別表示成功,失敗,處理中三種狀態(tài)
創(chuàng)建了一個(gè)promise對(duì)象,具有state、always、then、primise方法
擴(kuò)展primise對(duì)象生成最終的Deferred對(duì)象,返回該對(duì)象
這里其實(shí)就是3個(gè)處理,但是有個(gè)優(yōu)化代碼的地方,就是把共性的代碼給抽象出來,通過動(dòng)態(tài)生成了

具體源碼分析:

Deferred自身則圍繞這三個(gè)對(duì)象進(jìn)行更高層次的抽象

觸發(fā)回調(diào)函數(shù)列表執(zhí)行(函數(shù)名)
添加回調(diào)函數(shù)(函數(shù)名)
回調(diào)函數(shù)列表(jQuery.Callbacks對(duì)象)
deferred最終狀態(tài)(第三組數(shù)據(jù)除外)
vartuples=[
//action,addlistener,listenerlist,finalstate
["resolve","done",jQuery.Callbacks("oncememory"),"resolved"],
["reject","fail",jQuery.Callbacks("oncememory"),"rejected"],
["notify","progress",jQuery.Callbacks("memory")]
],
這里抽象出2組陣營(yíng):

1組:回調(diào)方法/事件訂閱

done,fail,progress
2組:通知方法/事件發(fā)布

resolve,reject,notify,resolveWith,rejectWith,notifyWith
tuples 元素集 其實(shí)是把相同有共同特性的代碼的給合并成一種結(jié)構(gòu),然后通過一次處理

jQuery.each(tuples,function(i,tuple){
varlist=tuple[2],
stateString=tuple[3];
promise[tuple[1]]=list.add;
if(stateString){
list.add(function(){
state=stateString;
//[reject_list|resolve_list].disable;progress_list.lock
},tuples[i^1][2].disable,tuples[2][2].lock);
}
deferred[tuple[0]]=function(){
deferredtuple[0]+"With";
returnthis;
};
deferred[tuple[0]+"With"]=list.fireWith;
});
對(duì)于tuples的3條數(shù)據(jù)集是分2部分處理的

第一部分將回調(diào)函數(shù)存入

promise[tuple[1]]=list.add;
其實(shí)就是給promise賦予3個(gè)回調(diào)函數(shù)

promise.done=$.Callbacks("oncememory").add
promise.fail=$.Callbacks("oncememory").add
promise.progressl=$.Callbacks("memory").add
如果存在deferred最終狀態(tài)

默認(rèn)會(huì)預(yù)先向doneList,failList中的list添加三個(gè)回調(diào)函數(shù)

if(stateString){
list.add(function(){
//state=[resolved|rejected]
state=stateString;
//[reject_list|resolve_list].disable;progress_list.lock
},tuples[i^1][2].disable,tuples[2][2].lock);
}


這里有個(gè)小技巧

i ^ 1 按位異或運(yùn)算符

所以實(shí)際上第二個(gè)傳參數(shù)是1、0索引對(duì)調(diào)了,所以取值是failList.disable與doneList.disable


通過stateString有值這個(gè)條件,預(yù)先向doneList,failList中的list添加三個(gè)回調(diào)函數(shù)

分別是:

doneList:[changeState,failList.disable,processList.lock]
failList:[changeState,doneList.disable,processList.lock]
changeState 改變狀態(tài)的匿名函數(shù),deferred的狀態(tài),分為三種:pending(初始狀態(tài)), resolved(解決狀態(tài)), rejected(拒絕狀態(tài))
不論deferred對(duì)象最終是resolve(還是reject),在首先改變對(duì)象狀態(tài)之后,都會(huì)disable另一個(gè)函數(shù)列表failList(或者doneList)
然后lock processList保持其狀態(tài),最后執(zhí)行剩下的之前done(或者fail)進(jìn)來的回調(diào)函數(shù)
所以第一步最終都是圍繞這add方法

done/fail/是list.add也就是callbacks.add,將回調(diào)函數(shù)存入回調(diào)對(duì)象中
第二部分很簡(jiǎn)單,給deferred對(duì)象擴(kuò)充6個(gè)方法

resolve/reject/notify 是 callbacks.fireWith,執(zhí)行回調(diào)函數(shù)
resolveWith/rejectWith/notifyWith 是 callbacks.fireWith 隊(duì)列方法引用
最后合并promise到deferred

promise.promise(deferred);
jQuery.extend(obj,promise)
所以最終通過工廠方法Deferred構(gòu)建的異步對(duì)象帶的所有的方法了

return 內(nèi)部的deferred對(duì)象了

由此可見我們?cè)?/p>

vardefer=$.Deferred();//構(gòu)建異步對(duì)象
的時(shí)候,內(nèi)部的對(duì)象就有了4個(gè)屬性方法了

deferred: Object
always: function () {
done: function () {
fail: function () {
notify: function () {
notifyWith: function ( context, args ) {
pipe: function ( / fnDone, fnFail, fnProgress / ) {
progress: function () {
promise: function ( obj ) {
reject: function () {
rejectWith: function ( context, args ) {
resolve: function () {
resolveWith: function ( context, args ) {
state: function () {
then: function ( / fnDone, fnFail, fnProgress / ) {
promise: Object
always: function () {
done: function () {
fail: function () {
pipe: function ( / fnDone, fnFail, fnProgress / ) {
progress: function () {
promise: function ( obj ) {
state: function () {
then: function ( / fnDone, fnFail, fnProgress / ) {
state: "pending"
tuples: Array[3]
構(gòu)造圖

以上只是在初始化構(gòu)建的時(shí)候,我們往下看看動(dòng)態(tài)執(zhí)行時(shí)候的處理

*執(zhí)行期***

一個(gè)最簡(jiǎn)單的demo為例子

vard=$.Deferred();
setTimeout(function(){
d.resolve(22)
},0);
d.then(function(val){
console.log(val);
})
當(dāng)延遲對(duì)象被 resolved 時(shí),任何通過 deferred.then或deferred.done 添加的 doneCallbacks,都會(huì)被調(diào)用。回調(diào)函數(shù)的執(zhí)行順序和它們被添加的順序是一樣的。傳遞給 deferred.resolve() 的 args 參數(shù),會(huì)傳給每個(gè)回調(diào)函數(shù)。當(dāng)延遲對(duì)象進(jìn)入 resolved 狀態(tài)后,再添加的任何 doneCallbacks,當(dāng)它們被添加時(shí),就會(huì)被立刻執(zhí)行,并帶上傳入給 .resolve()的參數(shù)

換句話說,我們調(diào)用d.resolve(22) 就等于是調(diào)用。

匿名函數(shù)并傳入?yún)?shù)值 22

function(val){
console.log(val);//22
}
當(dāng)前實(shí)際的使用中會(huì)有各種復(fù)雜的組合情況,但是整的外部調(diào)用流程就是這樣的

* resolve的實(shí)現(xiàn) ***

我們回顧下,其實(shí)Deferred對(duì)象,內(nèi)部的實(shí)現(xiàn)還是Callbacks對(duì)象,只是在外面再封裝了一層API,供接口調(diào)用

d.resolve(22)
實(shí)際上調(diào)用的就是通過這個(gè)代碼生成的

deferred[tuple[0]]=function(){
deferredtuple[0]+"With";
returnthis;
};
deferred[tuple[0]+"With"]=list.fireWith;
deferred.resolveWith()
最終執(zhí)行的就是 list.fireWith

callbacks.fireWith()
所以最終又回到回調(diào)對(duì)象callbacks中的私有方法fire()了

Callbacks會(huì)通過

callbacks.add()

把回調(diào)函數(shù)給注冊(cè)到內(nèi)部的list = []上,我們回來過看看

deferred.then()

d.then(function(val){
console.log(val);
})
* then的實(shí)現(xiàn) ***

then:function(/fnDone,fnFail,fnProgress/){
varfns=arguments;
returnjQuery.Deferred(function(newDefer){
jQuery.each(tuples,function(i,tuple){
varaction=tuple[0],
fn=jQuery.isFunction(fns[i])&&fns[i];
//deferred[done|fail|progress]forforwardingactionstonewDefer
deferred[tuple[1]](function(){
//省略............
});
});
fns=null;
}).promise();
},
遞歸jQuery.Deferred
傳遞了func
鏈?zhǔn)秸{(diào)用了promise()
因?yàn)樵诋惒綄?duì)象的方法都是嵌套找作用域?qū)傩苑椒ǖ?/p>

這里我額外的提及一下作用域

vard=$.Deferred();
這個(gè)異步對(duì)象d是作用域是如何呢?

第一層:無可爭(zhēng)議,瀏覽器環(huán)境下最外層是 window

第二層:jquery本身是一個(gè)閉包

第三層: Deferred工廠方法產(chǎn)生的作用域

如果用d.then()方法呢?

很明顯then方法又是嵌套在內(nèi)部的函數(shù),所以執(zhí)行的時(shí)候都默認(rèn)會(huì)包含以上三層作用域+自己本身函數(shù)產(chǎn)生的作用域了

我們用個(gè)簡(jiǎn)單圖描繪下

演示文稿1

根據(jù)規(guī)則,在最內(nèi)部的函數(shù)能夠訪問上層作用域的所有的變量

我們先從使用的層面去考慮下結(jié)構(gòu)設(shè)計(jì):

demo 1

vardefer=$.Deferred();
varfiltered=defer.then(function(value){
returnvalue*2;
});
defer.resolve(5);
filtered.done(function(value){
console.log(value)//10
});
demo 2

vardefer=$.Deferred();
defer.then(function(value){
returnvalue2;
}).then(function(value){
returnvalue2;
}).done(function(value){
alert(value)//20
});
defer.resolve(5);
其實(shí)這里就是涉及到defer.then().then().done() 鏈?zhǔn)秸{(diào)用了

API是這么定義的:

deferred.then(doneFilter[,failFilter][,progressFilter])
從jQuery 1.8開始, 方法返回一個(gè)新的promise(承諾),通過一個(gè)函數(shù),可以過濾deferred(延遲)的狀態(tài)和值。替換現(xiàn)在過時(shí)的deferred.pipe()方法。 doneFilter 和 failFilter函數(shù)過濾原deferred(延遲)的解決/拒絕的狀態(tài)和值。 progressFilter 函數(shù)過濾器的任何調(diào)用到原有的deferred(延遲)的notify 和 notifyWith的方法。 這些過濾器函數(shù)可以返回一個(gè)新的值傳遞給的 promise(承諾)的.done() 或 .fail() 回調(diào),或他們可以返回另一個(gè)觀察的對(duì)象(遞延,承諾等)傳遞給它的解決/拒絕的狀態(tài)和值promise(承諾)的回調(diào)。 如果過濾函數(shù)是空,或沒有指定,promise(承諾)將得到與原來值相同解決(resolved)或拒絕(rejected)。

我們抓住幾點(diǎn):

返回的是新的promise對(duì)象
內(nèi)部有一個(gè)濾器函數(shù)
從demo 1中我們就能看到

經(jīng)過x.then()方法處理的代碼中返回的this(filtered ),不是原來的$.Deferred()所有產(chǎn)生的那個(gè)異步對(duì)象(defer )了

所以,每經(jīng)過一個(gè)then那么內(nèi)部處理的this都要被重新設(shè)置,那么為什么要這樣處理呢?

源碼

then:function(/fnDone,fnFail,fnProgress/){
varfns=arguments;
//分別為deferred的三個(gè)callbacklist添加回調(diào)函數(shù),根據(jù)fn的是否是函數(shù),分為兩種情況
returnjQuery.Deferred(function(newDefer){
jQuery.each(tuples,function(i,tuple){
varaction=tuple[0],
fn=jQuery.isFunction(fns[i])&&fns[i];
//deferred[done|fail|progress]forforwardingactionstonewDefer
deferred[tuple[1]](function(){
varreturned=fn&&fn.apply(this,arguments);
if(returned&&jQuery.isFunction(returned.promise)){
returned.promise()
.done(newDefer.resolve)
.fail(newDefer.reject)
.progress(newDefer.notify);
}else{
newDefer[action+"With"](this===promise?newDefer.promise():this,fn?[returned]:arguments);
}
});
});
fns=null;
}).promise();
},
在Deferred傳遞實(shí)參的時(shí)候,支持一個(gè)flag,jQuery.Deferred(func)

傳遞一個(gè)回調(diào)函數(shù)

//Callgivenfuncifany
if(func){
func.call(deferred,deferred);
}
所以newDefer可以看作是

newDefer=$.Deferred();
那么func回調(diào)的處理的就是過濾函數(shù)了

deferred[tuple[1]](function(){
varreturned=fn&&fn.apply(this,arguments);
if(returned&&jQuery.isFunction(returned.promise)){
returned.promise()
.done(newDefer.resolve)
.fail(newDefer.reject)
.progress(newDefer.notify);
}else{
newDefer[action+"With"](this===promise?newDefer.promise():this,fn?[returned]:arguments);
}
});
這里其實(shí)也有編譯函數(shù)的概念,講未來要執(zhí)行的代碼,預(yù)先通過閉包函數(shù)也保存起來,使其訪問各自的作用域

第一步

分解tuples元素集

jQuery.each(tuples,function(i,tuple){
//過濾函數(shù)第一步處理
})
第二步

分別為deferred[ done | fail | progress ]執(zhí)行對(duì)應(yīng)的add方法,增加過濾函數(shù)給done | fail | progress 方法

deferred[tuple[1]](
傳入過濾函數(shù)
)//過濾函數(shù)執(zhí)行的時(shí)候在分解
代碼即

deferred[done]=list.add=callback.add
第三步

返回return jQuery.Deferred().promise()

此時(shí)構(gòu)建了一個(gè)新的Deferred對(duì)象,但是返回的的是經(jīng)過promise()方法處理后的,返回的是一個(gè)受限的promise對(duì)象

所以整個(gè)then方法就處理了2個(gè)事情

構(gòu)建一個(gè)新的deferred對(duì)象,返回受限的promise對(duì)象
給父deferred對(duì)象的[ done | fail | progress ]方法都增加一個(gè)過濾函數(shù)的方法
我們知道defer.then方法返回的是一個(gè)新的jQuery.Deferred().promise()對(duì)象

那么我們把defer.then返回的稱之為子對(duì)象,那么如何與父對(duì)象var defer = $.Deferred() 關(guān)聯(lián)的起來的

我看看源碼

deferredtuple[1]
deferred其實(shí)就是根級(jí)父對(duì)象的引用,所以就嵌套再深,其實(shí)都是調(diào)用了父對(duì)象deferred[ done | fail | progress 執(zhí)行add罷了

從圖中就能很明顯的看到 2個(gè)不同的deferred對(duì)象中 done fail progress分別都保存了不同的處理回調(diào)了

deferred.resolve( args )

當(dāng)延遲對(duì)象被 resolved 時(shí),任何通過 deferred.then或deferred.done 添加的 doneCallbacks,都會(huì)被調(diào)用
回調(diào)函數(shù)的執(zhí)行順序和它們被添加的順序是一樣的
傳遞給 deferred.resolve() 的 args 參數(shù),會(huì)傳給每個(gè)回調(diào)函數(shù)
當(dāng)延遲對(duì)象進(jìn)入 resolved 狀態(tài)后,再添加的任何 doneCallbacks,當(dāng)它們被添加時(shí),就會(huì)被立刻執(zhí)行,并帶上傳入給.resolve()的參數(shù)
流程如圖

流程解析:

1 執(zhí)行fire()方法,遞歸執(zhí)行l(wèi)ist所有包含的處理方法

2 執(zhí)行了默認(rèn)的 changeState, disable, lock 方法、

3 執(zhí)行過濾函數(shù)

根據(jù) var returned = fn.apply( this, arguments )的返回值(稱作returnReferred)是否是deferred對(duì)象

返回值是deferred對(duì)象,那么在returnReferred對(duì)象的三個(gè)回調(diào)函數(shù)列表中添加newDeferred的resolve(reject,notify)方法,也就是說newDeferrred的執(zhí)行依賴returnDeferred的狀態(tài)

不是函數(shù)的情況(如值為undefined或者null等),直接鏈接到newDeferred的resolve(reject,notify) 方法,也就是說 newDeferrred的執(zhí)行依賴外層的調(diào)用者deferred的狀態(tài)或者說是執(zhí)行動(dòng)作(resolve還是reject或者是notify) 此時(shí)deferred.then()相當(dāng)于將自己的callbacklist和newDeferred的callbacklist連接起來

下面就是嵌套deferred對(duì)象的劃分了

image

源碼還是要靠自己去折騰的,思想的提高比較難的,我們可以借鑒設(shè)計(jì)的思路,代碼書寫方式都是有益無害的。

流程的分析已經(jīng)比較透徹了,下一章在講解when的實(shí)現(xiàn)。

原文鏈接:http://www.cnblogs.com/aaronjs/p/3356505.html

轉(zhuǎn)載于:https://blog.51cto.com/5367378/2131552

與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的最细的实现剖析:jQuery 2.0.3源码分析Deferred的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。