• 【原创】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 });
  • 相关阅读:
    Android 的 ramdisk.img、system.img、userdata.img 作用说明,以及UBoot 系统启动过程
    Android启动过程以及各个镜像的关系
    程序员如何利用空余时间挣零花钱?
    hcharts实现堆叠柱形图
    [慕课笔记] node+mongodb建站攻略
    【每周一图】蜂鸟
    [慕课笔记]Node入口文件分析和目录初始化
    [慕课笔记] node+mongodb建站攻略
    hcharts实现堆叠柱形图
    程序员常用的六大技术博客类
  • 原文地址:https://www.cnblogs.com/lovesueee/p/2730287.html
Copyright © 2020-2023  润新知