jQuery.extend({
  /*
   * deferred对象的一大好处,就是它允许你自由添加多个回调函数。
   * $.ajax("test.html")
       .done(function(){ alert("哈哈,成功了!");} )
       .fail(function(){ alert("出错啦!"); } )
       .done(function(){ alert("第二个回调函数!");} );
   *deferred对象的另一大好处,就是它允许你为多个事件指定一个回调函数,这是传统写法做不到的。
      当两个操作都成功了才执行成功的回调函数,否则执行失败的回调函数
      $.when($.ajax("test1.html"), $.ajax("test2.html"))
       .done(function(){ alert("哈哈,成功了!"); })
       .fail(function(){ alert("出错啦!"); });
   *deferred对象的最大优点,就是它把这一套回调函数接口,从ajax操作扩展到了所有操作。
    也就是说,任何一个操作----不管是ajax操作还是本地操作,也不管是异步操作还是同步操作----
    都可以使用deferred对象的各种方法,指定回调函数。
   *deferred对象有三种执行状态----未完成,已完成和已失败。
   */
  Deferred: function( func ) {
    var tuples = [
    // action, add listener, listener list, final state

      //不同动作、监听、回调、最终状态组织起来的一个数组,将用它生成三组事件监听方法
      [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
      [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
      [ "notify", "progress", jQuery.Callbacks("memory") ]
    ],
    state = "pending",

    // 返回当前的执行状态
    promise = {
      state: function() {
      return state;
      },
      // 不管执行状态是resolve还是reject,都执行回调。
      always: function() {

        // deferred是在该回调函数中定义的一个对象(后面),它将作为返回值被返回,大部分方法将被绑定在该对象中
        deferred.done( arguments ).fail( arguments );
        return this;
      },
      // 参数可传入三个函数,第一个是成功的回调,第二个是失败的回调,第三个。。呃。。

      // 这个方法有些迷惑。。
      then: function( /* fnDone, fnFail, fnProgress */ ) {
        var fns = arguments;
        // 递归调用jQuery.Deferred()方法,并传入func。这些func将被加入回调列表中
        return jQuery.Deferred(function( newDefer ) {
          jQuery.each( tuples, function( i, tuple ) {
            var action = tuple[ 0 ],
              //判断参数是不是函数,如果是就赋值给fn,第一参数对应done,第二个参数对应fail,。。。
              fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
            deferred[ tuple[1] ](function() {
              //这里的this指,调用done、fail的对象,arguments指调用时传入的参数,比如函数。
              var returned = fn && fn.apply( this, arguments );
              if ( returned && jQuery.isFunction( returned.promise ) ) {
                returned.promise()
                  .done( newDefer.resolve )
                  .fail( newDefer.reject )
                  .progress( newDefer.notify );
              } else {
                newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
              }
            });
          });
          fns = null;
        }).promise();
      },
      // 提供一个方法将promise对象扩展到obj对象上。如果没有obj对象,则直接返回promise对象。
      promise: function( obj ) {
        return obj != null ? jQuery.extend( obj, promise ) : promise;
      }
    },

    // 所有封装的方法都会绑定到该对象上返回
    deferred = {};

    //为了向后兼容,以前都是用promise.pipe?
    promise.pipe = promise.then;

    // 遍历之前定义的动作、监听、回调、最终状态组成的数组,进行初始化操作
    jQuery.each( tuples, function( i, tuple ) {
      // 取得数组当中的 jQuery.Callbacks("once memory"),并将执行结果赋值给list。
      var list = tuple[ 2 ],
      //取出数组中的表示最终状态的字符串 “resolved rejected”
      stateString = tuple[ 3 ];

      //将list,也就是jQuery.Callback()方法返回的self对象中的add方法,绑定到promise.resolve/reject/notify上
      promise[ tuple[1] ] = list.add;

      // 如果stateString有值,以为数组中的第三个小数组中是没有最终状态值的,所以需要if做判断
      if ( stateString ) {

        // 调用Callbask返回的对象中的add方法,向回调列表中增加三个回调方法:一个匿名函数(改变最终状态),一个disable,一个lock
        list.add(function() {
          // state = [ resolved | rejected ]
          state = stateString;

          // [ reject_list | resolve_list ].disable; progress_list.lock
        }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
      }

      //将resolve()、reject()、notify()方法绑定到deferred对象上并暴露出来提供给用户调用
      deferred[ tuple[0] ] = function() {
        //调用deferred.resovleWith/rejectWith/notifyWith方法,这些方法是在下面定义的
        deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
        return this;
      };
      //将jQuery.Callback()方法返回的self对象中的fireWith方法绑定到deferred.resovleWith/rejectWith/notifyWith上
      deferred[ tuple[0] + "With" ] = list.fireWith;
    });

    // 将promise对象所有的方法和属性扩展到deferred对象上,使之具有promise所具有的方法和属性,但promise中并不会包含一些不想暴露的方法,所以使用时,可以返回deferred.promise对象
    promise.promise( deferred );

    //如果有传入的func,则在deferred对象执行环境执行func,支持另外一种使用Deferred的方法。例如:$.Deferred( function(){} );
    if ( func ) {
      func.call( deferred, deferred );
    }

    // 返回deferred对象
    return deferred;
  },

  //多个方法全都执行完毕后,执行回调函数
  when: function( subordinate /* , ..., subordinateN */ ) {

    ......

  }
});

jQuery的回调管理机制(二)的更多相关文章

  1. jQuery的回调管理机制(三)

    jQuery.when()方法是jQuery内部使用回调机制的范例. // 参数为多个方法,这些方法全部执行完成之后执行回调 when: function( subordinate /* , ..., ...

  2. jQuery的回调管理机制

    // 对option的一个缓存,避免每次都需要createOptions,option是创建Callback对象时的传入的参数// 每个option被存入optionsCache中类似于{memory ...

  3. Android包管理机制(二)PackageInstaller安装APK

    前言 在本系列上一篇文章Android包管理机制(一)PackageInstaller的初始化中我们学习了PackageInstaller是如何初始化的,这一篇文章我们接着学习PackageInsta ...

  4. 你必须了解的java内存管理机制(二)-内存分配

    前言 在上一篇文章中,我们花了较大的篇幅去介绍了JVM的运行时数据区,并且重点介绍了栈区的结构及作用,相关内容请猛戳!在本文中,我们将主要介绍对象的创建过程及在堆中的分配方式. 相关链接(注:文章讲解 ...

  5. x86保护模式 二 分段管理机制

    分段管理机制 段选择子和偏移地址的二维虚拟地址转换为一维的线性地址 一  段定义和虚拟地址到线性地址的转换 三个参数定义段:段基地址    段界限  和段属性    同时也是段描述符的结构 段基地址为 ...

  6. Android的包管理机制浅析(二)

    上篇刚好说到获取到了签名信息,以下进入安装过程,直接上源代码: private void installNewPackageLI(PackageParser.Package pkg, int pars ...

  7. 【python测试开发栈】—python内存管理机制(二)—垃圾回收

    在上一篇文章中(python 内存管理机制-引用计数)中,我们介绍了python内存管理机制中的引用计数,python正是通过它来有效的管理内存.今天来介绍python的垃圾回收,其主要策略是引用计数 ...

  8. ARC内存管理机制详解

    ARC在OC里面个人感觉又是一个高大上的牛词,在前面Objective-C中的内存管理部分提到了ARC内存管理机制,ARC是Automatic Reference Counting---自动引用计数. ...

  9. 浅谈Linux内存管理机制

    经常遇到一些刚接触Linux的新手会问内存占用怎么那么多?在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在这 ...

随机推荐

  1. [ACM] POJ 1611 The Suspects (并查集,输出第i个人所在集合的总人数)

    The Suspects Time Limit: 1000MS   Memory Limit: 20000K Total Submissions: 21586   Accepted: 10456 De ...

  2. C# 让枚举返回字符串

    下面的手段是使用给枚举项打标签的方式,来返回字符串 分别定义一个属性类,一个枚举帮助类 /// <summary> /// 自定义属性 /// </summary> [Attr ...

  3. 加密算法(对称加密)AES、DES (非对称加密)RSA、DSA

    目前主流的加密方式有:(对称加密)AES.DES        (非对称加密)RSA.DSA

  4. 炫酷霸气的HTML5/jQuery应用及源码

    也许在5年前,HTML5还是一种很前卫的技术,大家还只是将它当做实验来看待,更别说产品应用了.但是现在HTML5已经非常流行,无论从PC端还是移动端,HTML5都扮演着非常重要的角色.今天我们要分享的 ...

  5. Nginx防盗链 Nginx访问控制 Nginx解析php相关配置 Nginx代理

     

  6. 如何用BarTender将日期变量和序列号变量放一起打印成条码?

    刚接触BarTender 2016的小伙伴们可能对条码的数据源还不太搞的定,例如有时需要将日期变量和序列号变量放一起打印成条码,那如何简单达到目的呢?下面,小编教大家解决这一问题的三大步骤. 1.在B ...

  7. 关于移位运算符>>和<<

    首先,移位运算符有三种,其操作类型只支持:byte / short / char / int和long五种. << 左移运算符,表示将左边的操作数的二进制数据向左移动*位,移动后空缺位以0 ...

  8. oracle 产生随机数

    -- 产生一个任意大小的随机数select dbms_random.random from dual; -- 产生一个100以内的随机数select abs(mod(dbms_random.rando ...

  9. 改造phpcms-v9自带的字符串截取函数

    1.phpcms-v9自带的字符串截取函数在phpcms/libs/functions/global.func.php文件中: /** * 字符截取 支持UTF8/GBK * @param $stri ...

  10. python中字符串的几种表达方式(用什么方式表示字符串)

    说明: 今天在学习python的基础的内容,学习在python中如何操作字符串,在此记录下. 主要是python中字符串的几种表达,表示方式. python的几种表达方式 1 使用单引号扩起来字符串 ...