【原创】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 });
原文地址:https://www.cnblogs.com/lovesueee/p/2730287.html