成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

最細的實現剖析:jQuery 2.0.3源碼分析Deferred

開發 前端
源碼還是要靠自己去折騰的,思想的提高比較難的,我們可以借鑒設計的思路,代碼書寫方式都是有益無害的。流程的分析已經比較透徹。

Deferred的概念請看第一篇

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

******************構建Deferred對象時候的流程圖**************************

**********************源碼解析**********************

因為callback被剝離出去后,整個deferred就顯得非常的精簡

  1. jQuery.extend({   
  2.  
  3.     Deferred : function(){}  
  4.       
  5.     when : function()  
  6.  
  7. )} 

對于extend的繼承這個東東,在之前就提及過jquery如何處理內部jquery與init相互引用this的問題

對于JQ的整體架構一定要弄懂 http://www.cnblogs.com/aaronjs/p/3278578.html

所以當jQuery.extend只有一個參數的時候,其實就是對jQuery靜態方法的一個擴展

我們在具體看看2個靜態方法內部都干了些什么

Deferred整體結構:

源碼精簡了部分代碼

  1. Deferred: function( func ) {  
  2.         var tuples = [  
  3.                 // action, add listener, listener list, final state  
  4.                 [ "resolve""done", jQuery.Callbacks("once memory"), "resolved" ],  
  5.                 [ "reject""fail", jQuery.Callbacks("once memory"), "rejected" ],  
  6.                 [ "notify""progress", jQuery.Callbacks("memory") ]  
  7.             ],  
  8.             state = "pending",  
  9.             promise = {  
  10.                 state: function() {},  
  11.                 always: function() {},  
  12.                 then: function/* fnDone, fnFail, fnProgress */ ) { },  
  13.                 // Get a promise for this deferred  
  14.                 // If obj is provided, the promise aspect is added to the object  
  15.                 promise: function( obj ) {}  
  16.             },  
  17.             deferred = {};  
  18.         jQuery.each( tuples, function( i, tuple ) {  
  19.             deferred[ tuple[0] + "With" ] = list.fireWith;  
  20.         });  
  21.         promise.promise( deferred );  
  22.         // All done!  
  23.         return deferred;  
  24.     }, 
  1. 顯而易見Deferred是個工廠類,返回的是內部構建的deferred對象
  2. tuples 創建三個$.Callbacks對象,分別表示成功,失敗,處理中三種狀態
  3. 創建了一個promise對象,具有state、always、then、primise方法
  4. 擴展primise對象生成最終的Deferred對象,返回該對象

這里其實就是3個處理,但是有個優化代碼的地方,就是把共性的代碼給抽象出來,通過動態生成了

具體源碼分析:

Deferred自身則圍繞這三個對象進行更高層次的抽象

  • 觸發回調函數列表執行(函數名)
  • 添加回調函數(函數名)
  • 回調函數列表(jQuery.Callbacks對象)
  • deferred最終狀態(第三組數據除外)
  1. var tuples = [  
  2.         // action, add listener, listener list, final state  
  3.         [ "resolve""done", jQuery.Callbacks("once memory"), "resolved" ],  
  4.         [ "reject""fail", jQuery.Callbacks("once memory"), "rejected" ],  
  5.         [ "notify""progress", jQuery.Callbacks("memory") ]  
  6.     ], 

這里抽象出2組陣營:

1組:回調方法/事件訂閱  

  1. done,fail,progress 

2組:通知方法/事件發布    

  1. resolve,reject,notify,resolveWith,rejectWith,notifyWith 

tuples 元素集 其實是把相同有共同特性的代碼的給合并成一種結構,然后通過一次處理

  1. jQuery.each( tuples, function( i, tuple ) {  
  2.             var list = tuple[ 2 ],  
  3.                 stateString = tuple[ 3 ];  
  4.             promise[ tuple[1] ] = list.add;  
  5.             if ( stateString ) {  
  6.                 list.add(function() {  
  7.                     state = stateString;  
  8.  
  9.                 // [ reject_list | resolve_list ].disable; progress_list.lock  
  10.                 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );  
  11.             }  
  12.             deferred[ tuple[0] ] = function() {  
  13.                 deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );  
  14.                 return this;  
  15.             };  
  16.             deferred[ tuple[0] + "With" ] = list.fireWith;  
  17.         }); 

對于tuples的3條數據集是分2部分處理的

第一部分將回調函數存入

  1. promise[ tuple[1] ] = list.add; 

其實就是給promise賦予3個回調函數

  1. promise.done = $.Callbacks("once memory").add 
  1. promise.fail = $.Callbacks("once memory").add 
  1. promise.progressl = $.Callbacks("memory").add 

如果存在deferred最終狀態

默認會預先向doneList,failList中的list添加三個回調函數

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

*************************************************************
這里有個小技巧

i ^ 1 按位異或運算符

所以實際上第二個傳參數是1、0索引對調了,所以取值是failList.disable與doneList.disable

************************************************************* 

通過stateString有值這個條件,預先向doneList,failList中的list添加三個回調函數

分別是:

  1. doneList : [changeState, failList.disable, processList.lock]  
  2. failList : [changeState, doneList.disable, processList.lock] 
  • changeState 改變狀態的匿名函數,deferred的狀態,分為三種:pending(初始狀態), resolved(解決狀態), rejected(拒絕狀態)
  • 不論deferred對象最終是resolve(還是reject),在首先改變對象狀態之后,都會disable另一個函數列表failList(或者doneList)
  • 然后lock processList保持其狀態,最后執行剩下的之前done(或者fail)進來的回調函數

所以第一步最終都是圍繞這add方法

  • done/fail/是list.add也就是callbacks.add,將回調函數存入回調對象中

第二部分很簡單,給deferred對象擴充6個方法

最后合并promise到deferred

  1. promise.promise( deferred );  
  2. jQuery.extend( obj, promise ) 

所以最終通過工廠方法Deferred構建的異步對象帶的所有的方法了

return 內部的deferred對象了

由此可見我們在

  1. var defer = $.Deferred(); //構建異步對象 

的時候,內部的對象就有了4個屬性方法了

  1. deferred: Object
    1. always: function () {
    2. done: function () {
    3. fail: function () {
    4. notify: function () {
    5. notifyWith: function ( context, args ) {
    6. pipe: function ( /* fnDone, fnFail, fnProgress */ ) {
    7. progress: function () {
    8. promise: function ( obj ) {
    9. reject: function () {
    10. rejectWith: function ( context, args ) {
    11. resolve: function () {
    12. resolveWith: function ( context, args ) {
    13. state: function () {
    14. then: function ( /* fnDone, fnFail, fnProgress */ ) {
  2. promise: Object
    1. always: function () {
    2. done: function () {
    3. fail: function () {
    4. pipe: function ( /* fnDone, fnFail, fnProgress */ ) {
    5. progress: function () {
    6. promise: function ( obj ) {
    7. state: function () {
    8. then: function ( /* fnDone, fnFail, fnProgress */ ) {
  3. state: "pending"
  4. tuples: Array[3]

構造圖

以上只是在初始化構建的時候,我們往下看看動態執行時候的處理

#p#

*****************執行期***********************

一個最簡單的demo為例子

  1. var d = $.Deferred();  
  2.  
  3.  setTimeout(function(){  
  4.         d.resolve(22)  
  5.   },0);  
  6.  
  7.  d.then(function(val){  
  8.       console.log(val);  
  9.  }) 

當延遲對象被 resolved 時,任何通過 deferred.then或deferred.done 添加的 doneCallbacks,都會被調用。回調函數的執行順序和它們被添加的順序是一樣的。傳遞給 deferred.resolve() 的 args 參數,會傳給每個回調函數。當延遲對象進入 resolved 狀態后,再添加的任何 doneCallbacks,當它們被添加時,就會被立刻執行,并帶上傳入給 .resolve()的參數

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

匿名函數并傳入參數值 22

  1. function(val){  
  2.       console.log(val); //22  
  3.  } 

當前實際的使用中會有各種復雜的組合情況,但是整的外部調用流程就是這樣的

***************** resolve的實現 *******************

我們回顧下,其實Deferred對象,內部的實現還是Callbacks對象,只是在外面再封裝了一層API,供接口調用

  1. d.resolve(22) 

實際上調用的就是通過這個代碼生成的

  1. deferred[ tuple[0] ] = function() {  
  2.     deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );  
  3.     return this;  
  4. };  
  5.  
  6. deferred[ tuple[0] + "With" ] = list.fireWith; 
  1. deferred.resolveWith() 

最終執行的就是 list.fireWith

  1. callbacks.fireWith() 

所以最終又回到回調對象callbacks中的私有方法fire()了

 

Callbacks會通過

callbacks.add()

把回調函數給注冊到內部的list = []上,我們回來過看看

deferred.then()

  1. d.then(function(val){  
  2.       console.log(val);  
  3.  }) 

***************** then的實現 *******************

  1. then: function/* fnDone, fnFail, fnProgress */ ) {  
  2.     var fns = arguments;  
  3.     return jQuery.Deferred(function( newDefer ) {  
  4.         jQuery.each( tuples, function( i, tuple ) {  
  5.             var action = tuple[ 0 ],  
  6.                 fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];  
  7.             // deferred[ done | fail | progress ] for forwarding actions to newDefer  
  8.             deferred[ tuple[1] ](function() {  
  9.                    //省略............  
  10.             });  
  11.         });  
  12.         fns = null;  
  13.     }).promise();  
  14. }, 
  • 遞歸jQuery.Deferred
  • 傳遞了func
  • 鏈式調用了promise()

因為在異步對象的方法都是嵌套找作用域屬性方法的

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

  1. var d = $.Deferred(); 

這個異步對象d是作用域是如何呢?

第一層:無可爭議,瀏覽器環境下最外層是 window

第二層:jquery本身是一個閉包

第三層: Deferred工廠方法產生的作用域

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

很明顯then方法又是嵌套在內部的函數,所以執行的時候都默認會包含以上三層作用域+自己本身函數產生的作用域了

我們用個簡單圖描繪下

演示文稿1

根據規則,在最內部的函數能夠訪問上層作用域的所有的變量

我們先從使用的層面去考慮下結構設計:

demo 1

  1. var defer = $.Deferred();  
  2.  
  3.   var filtered = defer.then(function( value ) {  
  4.         return value * 2;  
  5.       });  
  6.  
  7.   defer.resolve( 5 );  
  8.  
  9.   filtered.done(function( value ) {  
  10.       console.log(value) //10  
  11.   }); 

demo 2

  1. var defer = $.Deferred();  
  2.  
  3.   defer.then(function(value) {  
  4.     return value * 2;  
  5.   }).then(function(value) {  
  6.     return value * 2;  
  7.   }).done(function(value) {  
  8.       alert(value)  //20  
  9.   });  
  10.  
  11.   defer.resolve( 5 ); 

其實這里就是涉及到defer.then().then().done()  鏈式調用了

API是這么定義的:

  1. deferred.then( doneFilter [, failFilter ] [, progressFilter ] ) 

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

我們抓住幾點:

  • 返回的是新的promise對象
  • 內部有一個濾器函數

從demo 1中我們就能看到

經過x.then()方法處理的代碼中返回的this(filtered ),不是原來的$.Deferred()所有產生的那個異步對象(defer )了

#p#

所以,每經過一個then那么內部處理的this都要被重新設置,那么為什么要這樣處理呢?

源碼

  1. then: function/* fnDone, fnFail, fnProgress */ ) {  
  2.                     var fns = arguments;  
  3.                     //分別為deferred的三個callbacklist添加回調函數,根據fn的是否是函數,分為兩種情況  
  4.                     return jQuery.Deferred(function( newDefer ) {  
  5.                         jQuery.each( tuples, function( i, tuple ) {  
  6.                             var action = tuple[ 0 ],  
  7.                                 fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];  
  8.                             // deferred[ done | fail | progress ] for forwarding actions to newDefer  
  9.                             deferred[ tuple[1] ](function() {  
  10.                                 var returned = fn && fn.apply( this, arguments );  
  11.                                 if ( returned && jQuery.isFunction( returned.promise ) ) {  
  12.                                     returned.promise()  
  13.                                         .done( newDefer.resolve )  
  14.                                         .fail( newDefer.reject )  
  15.                                         .progress( newDefer.notify );  
  16.                                 } else {  
  17.                                     newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );  
  18.                                 }  
  19.                             });  
  20.                         });  
  21.                         fns = null;  
  22.                     }).promise();  
  23.                 }, 

在Deferred傳遞實參的時候,支持一個flag,jQuery.Deferred(func)

傳遞一個回調函數

  1. // Call given func if any  
  2. if ( func ) {  
  3.     func.call( deferred, deferred );  

所以newDefer可以看作是

  1. newDefer = $.Deferred(); 

那么func回調的處理的就是過濾函數了

  1. deferred[ tuple[1] ](function() {  
  2.     var returned = fn && fn.apply( this, arguments );  
  3.     if ( returned && jQuery.isFunction( returned.promise ) ) {  
  4.         returned.promise()  
  5.             .done( newDefer.resolve )  
  6.             .fail( newDefer.reject )  
  7.             .progress( newDefer.notify );  
  8.     } else {  
  9.         newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );  
  10.     }  
  11. }); 

這里其實也有編譯函數的概念,講未來要執行的代碼,預先通過閉包函數也保存起來,使其訪問各自的作用域

第一步

分解tuples元素集

  1. jQuery.each( tuples, function( i, tuple ) {  
  2.    //過濾函數第一步處理  
  3. }) 

第二步

分別為deferred[ done | fail | progress ]執行對應的add方法,增加過濾函數給done | fail | progress 方法

  1. deferred[ tuple[1] ](  
  2. 傳入過濾函數  
  3. //過濾函數 執行的時候在分解 

代碼即

  1. deferred[done] = list.add = callback.add 

第三步

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

此時構建了一個新的Deferred對象,但是返回的的是經過promise()方法處理后的,返回的是一個受限的promise對象

所以整個then方法就處理了2個事情

  • 構建一個新的deferred對象,返回受限的promise對象
  • 給父deferred對象的[ done | fail | progress ]方法都增加一個過濾函數的方法

我們知道defer.then方法返回的是一個新的jQuery.Deferred().promise()對象

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

我看看源碼

  1. deferred[ tuple[1] ](//過濾函數//) 

deferred其實就是根級父對象的引用,所以就嵌套再深,其實都是調用了父對象deferred[ done | fail | progress 執行add罷了

 

從圖中就能很明顯的看到 2個不同的deferred對象中 done fail progress分別都保存了不同的處理回調了

deferred.resolve( args )

  • 當延遲對象被 resolved 時,任何通過 deferred.thendeferred.done 添加的 doneCallbacks,都會被調用
  • 回調函數的執行順序和它們被添加的順序是一樣的
  • 傳遞給 deferred.resolve() 的 args 參數,會傳給每個回調函數
  • 當延遲對象進入 resolved 狀態后,再添加的任何 doneCallbacks,當它們被添加時,就會被立刻執行,并帶上傳入給.resolve()的參數

流程如圖

 

流程解析:

1 執行fire()方法,遞歸執行list所有包含的處理方法

2 執行了默認的 changeState, disable, lock 方法、

3 執行過濾函數

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

  • 返回值是deferred對象,那么在returnReferred對象的三個回調函數列表中添加newDeferred的resolve(reject,notify)方法,也就是說newDeferrred的執行依賴returnDeferred的狀態

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

下面就是嵌套deferred對象的劃分了

image

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

流程的分析已經比較透徹了,下一章在講解when的實現。 

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

責任編輯:林師授 來源: 博客園
相關推薦

2013-09-16 10:40:44

Sizzle

2011-08-22 16:19:58

jQuery

2015-07-16 14:24:44

jQueryAlpha

2024-01-19 12:48:00

Redis存儲數據庫

2011-03-11 09:20:35

jQueryjavascript

2021-04-15 09:07:52

hotspotJavaC++

2022-09-27 18:56:28

ArrayList數組源代碼

2025-06-04 08:30:00

seata分布式事務開發

2021-10-26 18:22:25

原理注冊表信息

2020-08-26 14:00:37

C++string語言

2024-05-09 16:21:46

Deepseek技術算法

2012-09-06 10:07:26

jQuery

2021-02-26 13:59:41

RocketMQProducer底層

2025-03-31 00:00:00

MCPAPI服務器通信

2024-01-29 15:54:41

Java線程池公平鎖

2021-01-28 07:21:13

算法虛擬DOM前端

2022-04-29 14:56:40

通話應用源碼剖析

2020-10-09 14:13:04

Zookeeper Z

2018-10-31 15:54:47

Java線程池源碼

2011-04-21 10:45:34

ATLWTLCString
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 九九九久久国产免费 | 国产精品一区二区三级 | 日韩精品在线视频免费观看 | 国产精品久久久久久模特 | 99精品视频一区二区三区 | 久久久噜噜噜www成人网 | 欧美精品一二三 | 毛片在线视频 | 自拍偷拍第一页 | www日本在线 | 日本不卡免费新一二三区 | 国产高清在线精品一区二区三区 | 亚洲欧美在线视频 | 97国产爽爽爽久久久 | 久久久久99 | 欧美一区二区三区一在线观看 | 亚洲精品电影在线观看 | av男人天堂影院 | 成年人视频在线免费观看 | 狠狠涩 | 99精品一区 | 国产午夜精品久久久 | 日韩精品一区二区三区在线观看 | 在线看91| 成人激情视频网 | 欧美 日韩 国产 成人 在线 | 欧美日韩国产精品一区 | 日韩中文字幕第一页 | 成人av免费在线观看 | 日日草天天干 | 日韩一区二区在线视频 | 国产一区日韩在线 | 国产一级片免费看 | 国产伦精品一区二区三区照片91 | 精品日韩在线 | 国产免费一区 | 日韩不卡一区二区 | 另类视频区 | 成人免费一级视频 | 久夜精品 | 国产高清自拍视频在线观看 |