首先来一张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 });