首先來一張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 });