亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 網站 > WEB開發 > 正文

jQuery源碼學習:Deferred Object

2024-04-27 15:14:45
字體:
來源:轉載
供稿:網友

本文所有討論均基于jQuery版本3.1.1,官網http://jquery.com/。

一.Deferred Object

1. 簡介和創建

詳見API:http://api.jquery.com/jQuery.Deferred/。jQuery Deferred是”based onCommonJS PRomises/A design”,并不完全等同于ES6的Promise或者瀏覽器/JS引擎實現的原生Promise或是各類Promise庫。

jQuery.Deferred()工廠函數創建一個新的deferred object并且返回這個deferred:

jQuery.extend({    Deferred: function (func) {        var deferred = {};              // ...             // 實現deferred object的對象方法             // ...         return deferred;    }})

2. deferred與閉包

由于dererred的每個對象方法method都訪問了外部函數jQuery.Deferred()中的局部變量,因此jQuery.Deferred()在執行完畢后,其活動對象activation object不會被銷毀,而是被保存在deferred的每個method的[[Scope]]屬性中(僅保存指針)。這樣deferred的method在執行時就可以通過作用域鏈scope chain訪問到deferred的“私有變量”(參見javascript高級程序設計第三版7.4章)。

這里的“私有變量”不是靜態的,即每次通過調用jQuery.Deferred()得到的deferred都有自己的一份“私有變量”。

注1:這是在jQuery“塊級作用域”(參見Javascript高級程序設計第三版7.3章)里定義的局部變量:

var%20%20%20%20version%20=%20"3.1.1",%20%20%20%20//%20Define%20a%20local%20copy%20of%20jQuery%20%20%20%20jQuery%20=%20function%20(selector,%20context)%20{...},

注2:這是在jQuery“塊級作用域”里,通過下面的語句expose到全局環境下的變量:

%20%20%20%20window.jQuery%20=%20window.$%20=%20jQuery;

注3:jQuery.Deferred()返回的deferred%20object是jQuery.Deferred()的一個局部變量。

注4:deferred.promise()返回這個promise%20object。

3.%20deferred的私有變量

3.1.%20state

取三種值”pending”,”resolved”,”rejected”之一,分別對應Spec里的%20pending,fulfilled,%20or%20rejected;

可以通過.state()方法讀取;

初始狀態是”pendinig”。

3.2%20promise

可以通過.promise()讀取。和deferred一樣是jQuery.Deferred()的局部變量:

var%20promise%20=%20{%20%20%20%20...%20%20%20%20promise:%20function%20(obj)%20{%20%20%20%20%20%20%20%20return%20obj%20!=%20null%20?%20jQuery.extend(obj,%20promise)%20:%20promise;%20%20%20%20}},%20%20%20%20deferred%20=%20{};%20%20%20%20…%20%20%20//%20向deferred添加resolve,reject,notify,resolveWith,rejectWith和notifyWith方法%20%20%20%20//%20Make%20the%20deferred%20a%20promise%20%20%20%20promise.promise(deferred);

從上面jQuery.Deferred()的代碼可以看出,deferred對象copy了promise所有的方法;deferred和promise的方法指向相同的函數實例,因而deferred和deferred.promise()擁有同一份“私有變量”:

promise沒有可以改變state的方法,即promise沒有.resolve()/.resolveWith()/.reject()/.rejectWith()/.notify()/.notifyWith()方法。這樣返回deferred.promise()給其它代碼就可以確保其它代碼不會改變deferred的狀態或觸發回調函數的執行。

3.3%20tuples

var%20tuples%20=%20[%20%20%20%20%20%20%20%20//%20action,%20add%20listener,%20callbacks,%20%20%20%20%20%20%20%20//%20...%20.then%20handlers,%20argument%20index,%20[final%20state]%20%20%20%20%20%20%20%20["notify",%20"progress",%20jQuery.Callbacks("memory"),%20%20%20%20%20%20%20%20%20%20%20%20jQuery.Callbacks("memory"),%202],%20%20%20%20%20%20%20%20["resolve",%20"done",%20jQuery.Callbacks("once%20memory"),%20%20%20%20%20%20%20%20%20%20%20%20jQuery.Callbacks("once%20memory"),%200,%20"resolved"],%20%20%20%20%20%20%20%20["reject",%20"fail",%20jQuery.Callbacks("once%20memory"),%20%20%20%20%20%20%20%20%20%20%20%20jQuery.Callbacks("once%20memory"),%201,%20"rejected"]%20%20%20%20],

tuples數組存放了6個callback%20list%20object,為討論方便,我們命名

 %20 %20progress_callbacks%20= tuples[0][2],%20 %20progress_handlers%20= tuples[0][3]

 %20 %20fulfilled_callbacks%20= tuples[1][2],%20 %20 %20 %20fulfilled_handlers%20= tuples[1][3]

 %20 %20rejected_callbacks%20= tuples[2][2],%20 %20 %20rejected_handlers%20= tuples[2][3]

3.3.1%20callback%20list

以fulfilled_callbacks即tuples[1][2]為例,它是由jQuery.Callbacks(“once%20memory”)創建的一個callback%20list,這個callbacklist的.add()方法把回調函數加入到list中,而.fireWith()方法將導致list里的所有回調函數以類似于”先進先出”的順序執行。

3.3.2%20“once”%20flag

fulfilled_callbacks創建時帶有兩個flag:”once”和”memory”?!皁nce”表示這個callback%20list只能通過.fireWith()或.fire()的調用被fire一次,以后再次調用這個callback%20list的.fireWith()或.fire()沒有任何效果。

注意progress_callbacks和progress_handlers創建時沒有”once”這個flag,也就是說這兩個callback%20list可以被多次fire,每次fire時list里的所有回調函數順序執行。

3.3.3%20“memory”%20flag

callback%20list在fire時的context和arguments將被“記憶”下來,這樣在.fireWith()之后調用.add()的話,新加入的回調函數將馬上用“記住”的context和arguments執行。

%20%20%20%20fulfilled_callbacks%20=%20jQuery.Callbacks("once%20memory");%20%20%20%20fulfilled_callbacks.add(fn1);%20%20%20%20fulfilled_callbacks.add(fn2);%20%20%20%20fulfilled_callbacks.fireWith(context,%20args);%20%20%20//%20fn1先執行,即fn1.apply(context,args);然后fn2執行fn2.apply(context,args)%20 %20%20%20%20fulfilled_callbacks.fireWith(context,%20args);%20%20%20//%20沒有任何效果%20%20%20%20fulfilled_callbacks.add(fn3);%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20fn3馬上執行,即fn3.apply(context,%20args)

3.4.%20對象方法與私有變量

Deferred的對象方法

這個方法會調用

對狀態的影響

效果

.done()

fulfilled_callbacks.add()

不改變state

把回調函數加入到fulfilled_callbacks中

.resolve()/.resolveWith()

fulfilled_callbacks.fireWith()

state%20=%20“resovled”

fulfilled_callbacks中的所有回調函數被執行注1

fulfilled_handlers中的所有回調函數被執行

.fail()

rejected_callbacks.add()

不改變state

把回調函數加入到rejected_callbacks中

.reject()/.rejectWith()

rejected_callbacks.fireWith()

state%20=%20“rejected”

rejected_callbacks中的所有回調函數被執行注1

rejected_handlers中的所有回調函數被執行

.progress()

progress_callbacks.add()

不改變state

把回調函數加入到progress_callbacks中

.notify()/.notifyWith()

progress_callbacks.fireWith()

不改變state

progress_callbacks中的所有回調函數被執行

progress_handlers中的所有回調函數被執行

.then()/.catch()

fulfilled_handlers.add()

rejected_handlers.add()

progress_handlers.add()

不改變state

把回調函數加入到fulfilled_handlers中

把回調函數加入到rejected_handlers中

把回調函數加入到progress_handlers中

注1:這兩個方法如果重復執行的話,只有第一次的調用會有效果,原因見3.3.2。

4.%20鏈式調用

除了.state()和.promise()外,deferred的其它方法均返回一個deferred%20object,這樣deferred就可以支持鏈式調用,例如.always():

always:%20function%20()%20{%20%20%20%20deferred.done(arguments).fail(arguments);%20%20%20%20return%20this;},

調用.always()方法相當于同時向fulfilled_callbacks和rejected_callbacks中添加了一個同樣的回調函數,這樣無論這個deferred是被.resolve()/.resolveWith()還是.reject()/.rejectWith(),這個回調函數always將被執行。

需要注意的是大多數方法均返回this指針,也就是方法在哪個deferred上被調用,就返回哪個deferred。而.then()和.catch()方法返回的是一個新的deferred的promise。

%20%20%20%20dfd1%20=%20jQuery.Deferred();%20%20%20%20dfd1.done(fn)%20===%20dfd1;%20%20%20%20%20%20%20%20%20%20%20%20//true%20%20%20%20dfd1.then(fn)%20===%20dfd1.promise();%20//false

二.Control%20Flow

1.%20同步執行流程

這里的同步執行流程指的是在同一個event%20loop里被順序執行的代碼。由于是在一個event%20loop里單線程運行,這些代碼是同步執行的。例如deferred.resolve()/.resolveWith()被調用時,代碼總是按照下圖中箭頭指定的順序執行:

注1:也就是說,在deferred.resolve()/.resolveWith()之后,再去調用deferred.reject()/.rejectWith()將沒有任何效果。反之亦然。

注2:如果.notify()/.notifyWith()被調用過,那么.resolve()/.resolveWith()調用時不會disable掉progress_callbacks.add(),這樣仍然可以通過progress()方法加入progress的回調函數,而且這樣加入的回調函數會被立即執行。

    dfd1 = jQuery.Deferred();    dfd1.progress(function () {        console.log("progress1")    });    dfd1.notify();                    // console log "progress1"    dfd1.resolve();    dfd1.progress(function () {      // console log "progress2" immediately, i.e. before dfd1.progress() returns        console.log("progress2")    });

當然如果.notify()/.notifyWith()沒有被調用過,那么.resolve()/.resolveWith()調用后就無法通過.progress()方法加入progress的回調函數了。

2. .then()

2.1 .then()同步執行流程

jQuery.Deferred()調用時可以帶一個參數,這個參數指向一個函數func,在jQuery.Deferred()最后返回deferred之前會調用這個函數func。

Deferred: function (func) {    …    if (func) {        func.call(deferred, deferred);    }    // All done!    return deferred;},

.then()在最終返回新的deferredobject(其實是promise)之前,就調用了一個函數func:

then: function (onFulfilled, onRejected, onProgress) {          …return jQuery.Deferred(func).promises();}

.then()方法的同步執行流程如下:

用文字描述的話,deferred.then(onFulfilled)方法被調用時主要完成了兩件“任務”:

a. 把“包裹”了真正的回調函數onFulfilled的一個函數加入了deferred的“私有變量”fulfilled_handlers當中;

b. 產生了一個新的deferredobject即newDefer,并最終返回了newDefer.promise()。

當然這只是邏輯上的劃分,實際運行時.then()是同步執行的:“任務”b的前半段先執行(得到了newDefer),接下來“任務”a執行(需要newDefer作為實參),最后“任務”b的后半段執行,就如上圖所示。

2.2 加入到fulfilled_handlers中的函數

在上圖右側代碼部分,tuples[1][3]就是fulfilled_handlers,它的.add()方法將這個方法的參數加入到fulfilled_handlers中,也就是把執行resolve()函數得到的返回值加入到fulfilled_handlers中。而resolve()函數返回一個匿名的內部函數,所以,加入到fulfilled_handlers中的函數是執行resolve()函數返回的(resolve()函數的)內部函數。

function resolve(depth, deferred, handler, special) {    return function () {            …    };}

請注意這個resolve()函數本身是.then()方法的內部函數,并不是.resolve()方法。

 

3. 異步執行

3.1 異步的觸發

deferred.resolve()/resolveWith()執行時,會調用fulfilled_handlers,fire(),從而運行已加入到fulfilled_handlers中的函數。從上一節可知,此時運行的就是resolve()的內部匿名函數,如下:

function () {        var that = this,            args = arguments,            mightThrow = function () {…},            // Only normal processors (resolve) catch and reject exceptions            process = special ?                mightThrow :                function () {                    try {                        mightThrow();                    } catch (e) {…}                  }                };        // Support: Promises/A+ section 2.3.3.3.1        // https://promisesaplus.com/#point-57        // Re-resolve promises immediately to dodge false rejection from        // subsequent errors        if (depth) {            process();        } else {…            window.setTimeout(process);        }    };}

這個函數定義了兩個(對于它而言的)內部函數process()和mightThrow(),并在最后調用了window.setTimeout(),這樣process()作為定時器的回調函數被異步執行。

3.2 異步執行時的作用域鏈

3.3 process()的執行

3.3.1 fulfilled并且onFulfilledhandler返回了non-thenable

這種情況下,process()主要完成了兩個“任務”:

a. 運行先前.then()調用中傳入的handler即上圖中的onFulfilled,返回值賦給returned; 

    returned = handler.apply(that, args);

這里handler是.then()調用中傳入的onFulfilled,that是.resolveWith()時傳入的context,args是.resolveWith()時傳入的args,參考上圖。

b. 傳遞狀態,這是通過執行語句deferred.resolveWith(that, args)來完成的。這里deferred其實是.then()產生的newDefer,that是.resolveWith()時傳入的context,args是上一個任務中apply onFulfilled的返回returned,參考上圖。

3.3.2 fulfilled而且onFulfilledhandler返回了thenable

這種情況下,第一個任務和前一種情況是一樣的:

a. 運行先前.then()調用中傳入的handler即上圖中的onFulfilled,返回值賦給returned;

b. 遞歸。因為returned不是一個“可傳遞”的狀態(而是一個有著then方法的對象),無法直接傳遞給newDefer,只有等到returned這個thenable產生了一個狀態,并且這個狀態是“可傳遞”的(不是另外一個thenable),我們才能繼續狀態的傳續給newDefer。

這種情況下,下面的代碼將執行:

then.call(    returned,    resolve(maxDepth, deferred, Identity, special),    resolve(maxDepth, deferred, Thrower, special),    resolve(maxDepth, deferred, Identity,        deferred.notifyWith));

這里then是returned.then,deferred就是newDefer,Identify函數如下:

function Identity(v) {    return v;}

為方便起見,我們把上面這個call()調用中的第二個參數resolve()的返回結果叫做identity_wrapper_handler,那么上面這個call()大致等效于(僅考慮fulfilled情況)

    returned.then(identity_wrapper_handler);

step 1. identity_wrapper_handler將被異步調用,即在process()中會apply identity_wrapper_handler

step 2. identity_wrapper_handler是調用resolve(maxDepth, deferred, Identity, special)返回的函數,這里maxDepth=1,deferred就是newDefer,special是undefined,可參考上面的作用域鏈圖。這樣apply identity_wrapper_handler會導致process()再次被運行。

step 3. 但是這次handler是Identity,所以這次process()會apply Identity,根據上面Identify函數的定義,apply Identity將會返回returned被resolve時的狀態。換句話說,如果returned.reslove(y),那么apply Identity將會返回y。

step 4. 這時我們又回到了類似于process()第一次執行時情況:如果y不是一個thenable,那么狀態可以傳遞給newDefer;否則就重復3.3.2.b的“任務”,這樣就形成了遞歸。

對照規范https://promisesaplus.com/來看,3.3.2.b描述的就是規范中的”Promise Resolution Procedure”。如果認為deferred就是規范中的promise,returned就是規范中的x,returned.reslove(y)里的y就是規范中的y,那么3.3.2.b就實現了規范2.3所描述的從[[Resolve]](promise,x)到[[Resolve]](promise,y)。

從process()調用來看,先后發生了三次process()調用,每次process()調用時變量所指向/引用的對象如下表:

 

每次process()運行都先執行語句returned = handler.apply(that, args);

作用域鏈

handler

that

args

第一次運行時

onFulfilled

deferred.resolveWith(context, args)中的context

deferred.resolveWith(context, args)中的args

指向deferred.then()運行后形成的closures

第二次運行時

identity_wrapper_handler

returned.resolveWith(context, args)中的context

returned.resolveWith(context, args)中的args

指向returned.then()運行后形成的closures

第三次運行時

Identity

returned.resolveWith(context, args)中的context

returned.resolveWith(context, args)中的args

同第一次,即也指向deferred.then()運行后形成的closures

實現中還有一些細節,比如防止self thenable resolution,在follow thenable時避免false rejections,遵從協議的細節處理exceptions等等。

三.Deferred的應用

比如jQuery中ready方法還有Ajax等等都用到了deferred object。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久www成人免费精品张筱雨| 欧美一区亚洲一区| 国产免费一区视频观看免费| 91日本在线视频| 国产精品成人av在线| 国产精品久久久久久搜索| 欧美最猛性xxxxx(亚洲精品)| 色综合久综合久久综合久鬼88| 国产深夜精品福利| 日韩精品电影网| 久久福利网址导航| 九九热r在线视频精品| 亚洲天天在线日亚洲洲精| 日韩综合视频在线观看| 亚洲bt欧美bt日本bt| 久久九九国产精品怡红院| 久久综合久久八八| 亚洲春色另类小说| 国产视频久久久久久久| 国语自产精品视频在免费| 欧美激情日韩图片| 中文字幕日韩在线观看| 日韩中文娱乐网| 久久久人成影片一区二区三区| 日韩av123| 欧美大片网站在线观看| 中文字幕国产亚洲| 久久精品国产欧美激情| 亚洲国产成人av在线| 高清日韩电视剧大全免费播放在线观看| 在线观看中文字幕亚洲| 亚洲一级黄色av| 欧美电影免费观看电视剧大全| 97国产精品免费视频| 在线播放精品一区二区三区| 亚洲最大福利视频| 少妇高潮久久77777| 亚洲人午夜精品免费| 久久亚洲欧美日韩精品专区| 91久久久在线| 91精品国产综合久久香蕉的用户体验| 国产精品亚发布| 中文国产亚洲喷潮| 欧美高清电影在线看| 亚洲综合视频1区| 国产日产欧美a一级在线| 精品成人久久av| 国产午夜精品麻豆| 日韩国产精品亚洲а∨天堂免| 久久国产精品99国产精| 久久久免费观看视频| 欧美性受xxxx黑人猛交| 国产a级全部精品| 欧美高清在线视频观看不卡| 日韩有码视频在线| 欧美日韩综合视频| 欧美成人自拍视频| 亚洲精品福利在线| 欧美电影院免费观看| 91精品国产高清久久久久久91| 91精品在线国产| 国产精品电影网| 性视频1819p久久| 色先锋久久影院av| 精品丝袜一区二区三区| 欧美中文字幕第一页| 国内精品久久久久伊人av| 国产精品久久国产精品99gif| 国产精品成人国产乱一区| 亚洲第一区中文99精品| 日韩在线视频二区| 精品久久久av| 久操成人在线视频| 日韩风俗一区 二区| 97香蕉久久超级碰碰高清版| 国产精品美女无圣光视频| 在线电影欧美日韩一区二区私密| 久久久久久久久久国产| 黄色成人av在线| 日韩视频在线一区| 亚洲有声小说3d| 国产有码一区二区| 国产精品视频精品视频| 中文字幕在线精品| 久久久精品国产一区二区| 欧美一区二三区| 国产国语videosex另类| 亚洲人成网站777色婷婷| 亚洲成人激情视频| 国产999在线| 一区二区三区视频观看| 国产精品一区av| 亚洲国产天堂久久综合| 成人情趣片在线观看免费| 亚洲精品国偷自产在线99热| 欧美成人免费大片| 亚洲黄色www| 亚洲成人av中文字幕| 91精品国产乱码久久久久久蜜臀| 最近日韩中文字幕中文| 国产va免费精品高清在线| 一区三区二区视频| 亚洲www在线观看| 亚洲级视频在线观看免费1级| 欧美成aaa人片在线观看蜜臀| 欧美黑人性猛交| 久久6免费高清热精品| 欧美日韩日本国产| 亚洲欧美精品一区二区| 国产精品视频网址| 亚洲欧美国产一区二区三区| 亚洲欧美国产精品va在线观看| 国产精品伦子伦免费视频| 欧美丝袜一区二区| xxx成人少妇69| 美日韩精品视频免费看| 国产91精品高潮白浆喷水| 国产精品欧美亚洲777777| 91久久精品日日躁夜夜躁国产| 日韩成人在线免费观看| 国产成人一区二区三区电影| 国产精品成人免费电影| 91精品久久久久久久久久久| 成人激情视频在线观看| 黑人巨大精品欧美一区二区一视频| 国产精品久久精品| 91丨九色丨国产在线| 日韩精品中文字幕在线| 日日骚av一区| 日韩在线观看免费全集电视剧网站| 国产精品69久久久久| 久久天天躁狠狠躁夜夜爽蜜月| 久久人人爽国产| 欧美性xxxx极品hd欧美风情| 午夜精品美女自拍福到在线| 这里只有视频精品| 亚洲精品视频中文字幕| 亚洲欧美制服另类日韩| 国产精品视频精品| 欧美精品videos| 亚洲r级在线观看| 久久久精品欧美| 欧美日韩亚洲一区二区三区| 久久这里有精品视频| 在线观看国产精品91| 国产精品视频一区二区高潮| 国产欧美日韩丝袜精品一区| www.久久久久久.com| 欧美激情久久久久久| 欧洲中文字幕国产精品| 国产主播喷水一区二区| 色婷婷综合久久久久中文字幕1| 色综合久综合久久综合久鬼88| 欧美男插女视频| 久久亚洲私人国产精品va| 黄色91在线观看| 欧美日韩国产丝袜美女| 亚洲人a成www在线影院| 亚洲综合精品一区二区| 亚洲国产精品成人一区二区| 国产午夜精品美女视频明星a级| 一本色道久久综合狠狠躁篇的优点| 精品国产欧美一区二区三区成人| 欧洲成人性视频|