【原創】jQuery1.8.2源碼解析之jQuery.Deferred


首先來一張jQuery.Deferred的結構圖:

 

再來一張執行deferred.then(/*fnDone, fnFail, fnProcess*/)后的結構圖:

 

最后來看看源代碼:

  1 jQuery.extend({
  2 
  3     Deferred: function( func ) {
  4         // 數據元組集
  5         // 每個元組分別包含一些與當前deferred相關的信息: 
  6         // 分別是:觸發回調函數列表執行(函數名),添加回調函數(函數名),回調函數列表(jQuery.Callbacks對象),deferred最終狀態(第三組數據除外)
  7         // 總體而言,三個元組會有對應的三個callbacklist對應於doneList, failList, processList
  8         // 對於jQuery.Callbacks對象,可以看之前的文章http://www.cnblogs.com/lovesueee/archive/2012/10/18/2729829.html
  9         var tuples = [
 10                 // action, add listener, listener list, final state
 11                 [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
 12                 [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
 13                 [ "notify", "progress", jQuery.Callbacks("memory") ]
 14             ],
 15             // deferred的狀態,分為三種:pending(初始狀態), resolved(解決狀態), rejected(拒絕狀態)
 16             state = "pending",
 17             // promise對象,主要有兩點作用:
 18             // 1. 在初始化deferred對象時,promise對象里的方法都會被extend到deferred中去,作為引用(見86行)
 19             // 2. 那么,生成的deferred對象里必然引用了promise對象的promise方法,所以當調用deferred.promise()時,
 20             //    deferred對象會通過閉包返回promise對象,這就是所謂的受限制的deferred對象(用deferred2表示),因為相比之前,
 21             //    返回的deferred2不在擁有resolve(With), reject(With), notify(With)這些能改變deferred對象狀態並且執行callbacklist的方法了
 22             promise = {
 23                 // 返回閉包里的內部state(外部只讀)
 24                 state: function() {
 25                     return state;
 26                 },
 27                 // 同時在doneList和failList的list里添加回調函數(引用)
 28                 // 那么不論deferred最終狀態是resolved還是rejected, 回調函數都會被執行,這就是所謂的always
 29                 always: function() {
 30                     deferred.done( arguments ).fail( arguments );
 31                     return this;
 32                 },
 33                 // jQuery.then()會創建一個新的受限制的deferred對象
 34                 // 有點復雜,下面我有畫一個圖幫忙理解
 35                 then: function( /* fnDone, fnFail, fnProgress */ ) {
 36                     var fns = arguments;
 37                     // 創建新的受限制的deferred對象(稱作newDeferrred),並返回
 38                     // 利用返回的deferred對象就可以做很多事了,你懂的
 39                     return jQuery.Deferred(function( newDefer ) {
 40                         jQuery.each( tuples, function( i, tuple ) {
 41                             var action = tuple[ 0 ],
 42                                 fn = fns[ i ];
 43                             // deferred[ done | fail | progress ] for forwarding actions to newDefer
 44                             // 分別為deferred的三個callbacklist添加回調函數,根據fn的是否是函數,分為兩種情況:
 45                             // 1.不是函數的情況(如值為undefined或者null等),直接鏈接到newDeferred的resolve(reject,notify)方法,也就是說
 46                             //   newDeferrred的執行依賴外層的調用者deferred的狀態或者說是執行動作(resolve還是reject或者是notify)
 47                             //   此時deferred.then()相當於將自己的callbacklist和newDeferred的callbacklist連接起來了,故可以在newDeferred
 48                             //   中大做文章
 49                             // 2.是函數的情況,根據返回值(稱作returnReferred)是否是deferred對象,又可以分為兩種情況:
 50                             //   2.1 返回值是deferred對象,那么在returnReferred對象的三個回調函數列表中添加newDeferred的resolve(reject,notify)方法
 51                             //       也就是說newDeferrred的執行依賴returnDeferred的狀態
 52                             //   2.2 返回值不是deferred對象,那么將返回值returned作為newDeferred的參數並將從外層deferred那邊的上下文環境作為newDeferred
 53                             //       的執行上下文,然后執行對應的回調函數列表,此時newDeferrred的執行依賴外層的調用者deferred的狀態
 54                             deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
 55                                 function() {
 56                                     var returned = fn.apply( this, arguments );
 57                                     if ( returned && jQuery.isFunction( returned.promise ) ) {
 58                                         returned.promise()
 59                                             .done( newDefer.resolve )
 60                                             .fail( newDefer.reject )
 61                                             .progress( newDefer.notify );
 62                                     } else {
 63                                         newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
 64                                     }
 65                                 } :
 66                                 newDefer[ action ]
 67                             );
 68                         });
 69                         fns = null;
 70                     }).promise();
 71                 },
 72                 // Get a promise for this deferred
 73                 // If obj is provided, the promise aspect is added to the object
 74                 promise: function( obj ) {
 75                     return typeof obj === "object" ? jQuery.extend( obj, promise ) : promise;
 76                 }
 77             },
 78             // 實際返回的deferred對象
 79             deferred = {};
 80 
 81         // Keep pipe for back-compat
 82         // pipe和then引用同一個函數,所以功能是一樣的
 83         // 只不過通常的用法是:會用pipe進行filter操作
 84         promise.pipe = promise.then;
 85 
 86         // Add list-specific methods
 87         // 通過上面定義的數據元組集來擴展一些方法
 88         jQuery.each( tuples, function( i, tuple ) {
 89             var list = tuple[ 2 ],
 90                 stateString = tuple[ 3 ];
 91 
 92             // promise[ done | fail | progress ] = list.add
 93             // 給上面的promise對象添加done,fail,process方法
 94             // 這三個方法分別引用三個不同jQuery.Callbacks對象的add方法(不是同一個引用),
 95             // 那么這三個方法的用途就是向各自的回調函數列表list(各自閉包中)中添加回調函數,互不干擾
 96             promise[ tuple[1] ] = list.add;
 97 
 98             // Handle state
 99             // 通過stateString有值這個條件,預先向doneList,failList中的list添加三個回調函數
100             // doneList : [changeState, failList.disable, processList.lock]
101             // failList : [changeState, doneList.disable, processList.lock]
102             // changeState 指的是下面首先添加的一個改變deferred對象的匿名函數
103             // 可以看的出: 不論deferred對象最終是resolve(還是reject),在首先改變對象狀態之后,都會disable另一個函數列表failList(或者doneList)
104             // 然后lock processList保持其狀態,最后執行剩下的之前done(或者fail)進來的回調函數
105             // 當然了,上述情況processList除外
106             if ( stateString ) {
107                 list.add(function() {
108                     // state = [ resolved | rejected ]
109                     state = stateString;
110 
111                 // [ reject_list | resolve_list ].disable; progress_list.lock
112                 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
113             }
114 
115             // deferred[ resolve | reject | notify ] = list.fire
116             // 給deferred對象添加resolve(With), reject(With), notify(With)方法
117             // 這三個方法分別引用三個不同jQuery.Callbacks對象的fire方法(不是同一個引用),
118             // 那么這三個方法的用途就是執行各自的回調函數列表,互不干擾
119             deferred[ tuple[0] ] = list.fire;
120             deferred[ tuple[0] + "With" ] = list.fireWith;
121         });
122 
123         // Make the deferred a promise
124         // 將上面的promise對象extend進deferred中
125         promise.promise( deferred );
126 
127         // Call given func if any
128         // 如果調用jQuery.Deferred(func)指定了參數,那么調用func並設置func的上下文和參數均為deferred
129         // 在jQuery.then()中有用到這一點
130         if ( func ) {
131             func.call( deferred, deferred );
132         }
133 
134         // All done!
135         // 返回最終的deferred對象
136         return deferred;
137     },
138 
139     // Deferred helper
140     // 參數:一個(或多個)deferred對象(或其他)
141     // 當傳入的所有deferred對象都resolve或者reject了,執行when()創建的deferred對象(稱之為whenDeferred)對應的回調函數列表(非deferred對象被認為是resolve了)
142     when: function( subordinate /* , ..., subordinateN */ ) {
143         var i = 0,
144             // 首先將arguments偽數組轉換為真正的數組
145             resolveValues = core_slice.call( arguments ),
146             length = resolveValues.length,
147 
148             // the count of uncompleted subordinates
149             // jQuery.isFunction( subordinate.promise )用來判斷subordinate是否是deferred對象
150             // 1. 在參數個數等於1的情況下:
151             //   1.1 如果參數是deferred對象,那么remaining = length, 這是remaining就是1嘛
152             //   1.2 否則remaining為0
153             // 2. 在參數不等於1(即等於0或者大於1)的情況:remaining = length
154             remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
155 
156             // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
157             // 到這里就可以知道:如果參數個數僅為1個,並且是deferred對象,那么就無需再生成deferred對象
158             deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
159 
160             // Update function for both resolve and progress values
161             updateFunc = function( i, contexts, values ) {
162                 // 這里返回一個函數作為一個callback完全是為了創建一個閉包,主要是為了保持i的值
163                 return function( value ) {
164                     // 保存各個deferred執行的上下文,也就是說之后whenDeferred的回調函數的上下文就是一個數組
165                     contexts[ i ] = this;
166                     // 保存各個deferred執行時的參數,之后傳遞給whenDeferred的回調函數
167                     // 此時values的值有原先的jQuery.when()傳進來的參數變為各個deferred執行回調時的參數了,也就是說覆蓋了
168                     values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
169                     if( values === progressValues ) {
170                         // 這里,暫時不理解有什么用處?
171                         deferred.notifyWith( contexts, values );
172                     } else if ( !( --remaining ) ) {
173                         // 時機成熟,即所有延遲都resolve,執行whenDeferred的回調函數
174                         deferred.resolveWith( contexts, values );
175                     }
176                 };
177             },
178 
179             progressValues, progressContexts, resolveContexts;
180 
181         // add listeners to Deferred subordinates; treat others as resolved
182         // 如果參數個數大於1,那么就是說有可能存在多個deferred對象
183         // 這時需要一些條件判斷以保證是所有的deferred對象都resolve了,再執行whenDeferred的resolve
184         // 或者當有一個deferred對象reject了,whenDeferred的reject
185         if ( length > 1 ) {
186             progressValues = new Array( length );
187             progressContexts = new Array( length );
188             resolveContexts = new Array( length );
189             for ( ; i < length; i++ ) {
190                 // 如果是deferred對象
191                 if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
192                     // 給每個參數(deferred對象)添加最后的回調,用來檢查此時的狀態
193 
194                     resolveValues[ i ].promise()
195                         // 用於當每一個deferred對象resolve回來,用updateFunc返回的函數檢查此時其他deferred對象的狀態(即此時remaining是否等於0了)
196                         // 如果等於0,則執行whenDeferred的resolve,否則繼續等待
197                         .done( updateFunc( i, resolveContexts, resolveValues ) )
198                         // 如果有一個deferred對象reject,whenDeferred將執行reject
199                         .fail( deferred.reject )
200                         // 用於通知,暫時不知道有什么用?
201                         .progress( updateFunc( i, progressContexts, progressValues ) );
202                 // 如果不是deferred對象,直接--remaining,視為resolve
203                 } else {
204                     --remaining;
205                 }
206             }
207         }
208 
209         // if we're not waiting on anything, resolve the master
210         // 如果此時remaining就等與0了,表示沒有什么延遲需要等待,那么立即之行whenDeferred的resolveWith
211         // 此時resolveContexts為undefined, 這就意味這上下文將為全局的window
212         if ( !remaining ) {
213             deferred.resolveWith( resolveContexts, resolveValues );
214         }
215 
216         // 返回受限制的deferred對象
217         return deferred.promise();
218     }
219 });


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM