请接着上一课继续看。

$.Deferred()方法中,有两个对象,一个是deferred对象,一个是promise对象。

promise对象有以下几个方法:state,always,then,promise,pipe,done,fail,progress。

deferred对象除了有promise对象的所有方法外(通过jQuery.extend( obj, promise ),把promise对象的所有方法复制到deferred对象中),还有其他三个:resolve,reject,notify。

他们之间的区别是什么?我们先来看一个例子;

function a(){

  var cb = $.Deferred();

  setTimeout(function(){

    cb.resolve();

  },1000)

  return cb;

}

var newCB = a();

newCB.done(function(){

  alert("成功");

}).fail(function(){

  alert("失败")

});

以上代码,在一秒后,会弹出成功。但是如果我们在代码的最后再加上newCB.reject();只会弹出失败,成功不会弹出了(因为先执行reject方法,而延迟对象的实现是once的,所以resolve调用后,不会执行弹出成功的方法)。以上我们就可以知道,我们可以在代码中改变延迟对象的状态(本来是弹出成功,但是弹出了失败)。

解决办法:我们在a方法中,return时,我们return cb.promise()。这时,延迟对象cb就不会改变状态了。你在代码最后面加上cb.reject会报错(因为promise对象没有resolve,reject,notify方法,只有deferred对象有)。

从上可知,deferred对象可以改变状态,promise对象不能改变状态。

cb.promise()方法源码是:

promise: function( obj ) {
  return obj != null ? jQuery.extend( obj, promise ) : promise;
}

返回的就是promise对象。

接下来,我们来看下延迟对象的状态是如何变化的:

延迟对象总共有三个状态:默认为pending。成功为resolved,失败为rejected。

源代码里面是这样改变的:

var tuples = [
  [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
  [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
  [ "notify", "progress", jQuery.Callbacks("memory") ]
],

state = "pending",

jQuery.each( tuples, function( i, tuple ) {
  var list = tuple[ 2 ],
  stateString = tuple[ 3 ];    //resolved,rejected

  promise[ tuple[1] ] = list.add;  //经过下面的add添加,promise = {"done":[functon(){},fail.disable,progress.lock],"fail":[functon(){},done.disable,progress.lock]}

  if ( stateString ) {      //进入到if语句,能进入到if语句的就是resolved状态和rejected状态
    list.add(function() {  
          state = stateString;

          tuples[0][2] = jQuery.Callbacks("once memory")

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

       //往list中添加三个方法。第一个方法就是改变状态。因为成功状态和失败状态只能出现一个,并且只有一次,因此后面的两个方法是来实现不能进行状态的改变。其中tuples[ 2 ][ 2 ].lock,其实就是jQuery.Callbacks("memory").lock,意思就是进行中的后面的fire方法无效,不能再触发fire了。而第二个方法disable是禁止回调的所有功能。i=0或1,当 i=0时,代表成功的状态,tuples[1][2]= jQuery.Callbacks("once memory"),这时代表失败的状态(fail)的所有功能都不能用。当i=1时,代表失败的状态,那么tuples[0][2]代表成功的状态(done)的所有功能都不能用。意思就是,当执行了resolve时,它代表成功状态,这时reject(状态会变成失败)不会再触发了。

    //更简单的理解是:当resolve调用后,状态变成resolved,然后失败状态的reject,fail有关的任何方法都不能执行了,而且进行中状态的fire方法(notify方法)也不能执行了但是它的progress添加的方法,还是可以执行的(如果notfy方法在resolve方法之前调用)。
  }

  .....

}

always方法也是用来添加方法,比如:cb.always(function(){}),但是,这个方法的意思是,不管是成功还是失败,它里面的方法都会执行。

always: function() {
    deferred.done( arguments ).fail( arguments );
    return this;
},

then方法:cb.then(function(){成功},function(){失败},function(){进行中});可以同时添加成功,失败,进行中三个回调方法。

then: function( fnDone, fnFail, fnProgress) {
    var fns = arguments;
    return jQuery.Deferred(function( newDefer ) {    //返回一个无法改变状态的延迟对象promise,在回调方法中,传入newDefer是一个新的延迟对象deferred
      jQuery.each( tuples, function( i, tuple ) {   //第一次循环时,i=0,tuple=[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ]
        var action = tuple[ 0 ],     //resolve
        fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
        deferred[ tuple[1] ](function() {    //调用老的延迟对象deferred的done方法,也就是deferred.done(function(){}),所以老的deferred调用resolve时,会执行这个回调方法,而这个回调方法,会执行新的延迟对象newDefer中通过done添加的回调方法
          var returned = fn && fn.apply( this, arguments );    //执行then(pipe)添加的方法,这里是function(){成功}
          if ( returned && jQuery.isFunction( returned.promise ) ) {   //如果返回值是一个延迟对象就进入if语句
            returned.promise()    //如果返回的是一个延迟对象returned,就得到它的promise,然后就用一个hash跟新的延迟对象进行映射。promise的resolve一调用,就会执行newDefer的resolve,就会执行newDefer通过done添加的方法。
              .done( newDefer.resolve )
                .fail( newDefer.reject )
                  .progress( newDefer.notify );
          }

          else {      //如果返回的是字符串,newDefer[resolveWith](执行上下文, [返回值]),而延迟对象的resolveWith方法就是list.fireWith方法,就会执行newDefer.done添加的方法fn。并把参数值(returned字符串)传进这个回调方法fn。
            newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
            }
        });
      });
      fns = null;
    }).promise();
},

promise.pipe = promise.then;    //pipe方法就是then方法。

延迟对象的pipe方法的使用:

var cb = $.Deferred();

setTimeout(function(){

  cb.resolve("hi ");   //本来这里是触发cb.done添加的方法,但是触发了newCB.done添加的方法

},1000)

var newCB = cb.pipe(function(){    //pipe方法的意思是管道,意思是可以扩展延迟对象。这里就扩展了cb使之第一次参数后面有chaojidan字符串.因此扩展出来的新的延迟对象newCB就会在第一个参数后面加上chaojidan

  return arguments[0] + "chaojidan";

})

newCB.done(function(){

  alert(arguments[0]);    //这里会弹出hi chaojidan。因为newCB是cb扩展出来的,也就是继承出来的,所以cb.resolve会触发newCB.done添加的方法。

})

以上此方法then源码比较绕,对前端开发没什么帮助,就是一些逻辑上的处理,可以忽略掉。而且这个方法使用的也比较少,知道怎么使用,知道方法是用来干嘛的就行了。

加油!

jquery源码解析:jQuery延迟对象Deferred(工具方法)详解2的更多相关文章

  1. jquery源码解析:expando,holdReady,ready详解

    jQuery的工具方法,其实就是静态方法,源码里面就是通过extend方法,把这些工具方法添加给jQuery构造函数的. jQuery.extend({       //当只有一个对象时,就把这个对象 ...

  2. jquery源码解析:addClass,toggleClass,hasClass详解

    这一课,我们将继续讲解jQuery对元素属性操作的方法. 首先,我们先看一下这几个方法是如何使用的: $("#div1").addClass("box1 box2&quo ...

  3. jQuery 源码分析(十六) 事件系统模块 底层方法 详解

    jQuery事件系统并没有将事件监听函数直接绑定到DOM元素上,而是基于数据缓存模块来管理监听函数的,事件模块代码有点多,我把它分为了三个部分:分底层方法.实例方法和便捷方法.ready事件来讲,好理 ...

  4. jQuery 源码解析(二十七) 样式操作模块 坐标详解

    样式操作模块可用于管理DOM元素的样式.坐标和尺寸,本节讲解一下坐标这一块. 对于坐标来说,jQuery提供了一个offset方法用于获取第一个匹配元素的坐标或者设置所有匹配元素的坐标,还有offse ...

  5. jQuery 源码解析(三十一) 动画模块 便捷动画详解

    jquery在$.animate()这个接口上又封装了几个API,用于进行匹配元素的便捷动画,如下: $(selector).show(speed,easing,callback)        ;如 ...

  6. Spring源码解析二:IOC容器初始化过程详解

    IOC容器初始化分为三个步骤,分别是: 1.Resource定位,即BeanDefinition的资源定位. 2.BeanDefinition的载入 3.向IOC容器注册BeanDefinition ...

  7. tp6源码解析-第二天,ThinkPHP6编译模板流程详解,ThinkPHP6模板源码详解

    TP6源码解析,ThinkPHP6模板编译流程详解 前言:刚开始写博客.如果觉得本篇文章对您有所帮助.点个赞再走也不迟 模板编译流程,大概是: 先获取到View类实例(依赖注入也好,通过助手函数也好) ...

  8. jQuery 源码分析(十五) 数据操作模块 val详解

    jQuery的属性操作模块总共有4个部分,本篇说一下最后一个部分:val值的操作,也是属性操作里最简单的吧,只有一个API,如下: val(vlaue)        ;获取匹配元素集合中第一个元素的 ...

  9. jQuery 源码分析(十三) 数据操作模块 DOM属性 详解

    jQuery的属性操作模块总共有4个部分,本篇说一下第2个部分:DOM属性部分,用于修改DOM元素的属性的(属性和特性是不一样的,一般将property翻译为属性,attribute翻译为特性) DO ...

  10. LiteDB源码解析系列(2)数据库页详解

    在这一篇里,我将用图文的方式展示LiteDB中页的结构及作用,内容都是原创,在描述的过程中有不准确的地方烦请指出. 1.LiteDB页的技术工作原理 LiteDB虽然是单个文件类型的数据库,但是数据库 ...

随机推荐

  1. java算法 第七届 蓝桥杯B组(题+答案) 5.抽签

    5.抽签  (代码填空) X星球要派出一个5人组成的观察团前往W星.其中:A国最多可以派出4人.B国最多可以派出2人.C国最多可以派出2人..... 那么最终派往W星的观察团会有多少种国别的不同组合呢 ...

  2. What is _MainTex_ST ?

    [What is _MainTex_ST ?] 在Unity自带的Unlit/Texture中,有引用到float4 _MainTex_ST,如下: // Unlit shader. Simplest ...

  3. 【uva1658 算法竞赛入门经典】海军上将【费用流】

    题意 给出一个v(3<=v<=1000)个点e(3<=e<=10000)条边的有向加权图,求1-v的两条不相交(除了起点和终点外没有公共点)的路径,使得权和最小. 分析 费用流 ...

  4. cs api 之一

    无法创建   无法创建网络   执行顺序  

  5. Python设计模式之"外观模式"实例讲解

    Python中设计模式之外观模式主张以分多模块进行代码管理而减少耦合,下面用实例来进行说明. 应用特性: 在很多复杂而小功能需要调用需求时,而且这些调用往往还有一定相关性,即一调用就是一系列的. 结构 ...

  6. [Training Video - 6] [File Reading] [Java] Read Properties file

    package com.file.properties; import java.io.FileInputStream; import java.util.Properties; public cla ...

  7. 删除 Ceph 的image报rbd: error: image still has watchers

    在Ceph集群日常运维中,管理员可能会遇到有的image删除不了的情况,有一种情况是由于image下有快照信息,只需要先将快照信息清除,然后再删除该image即可,还有一种情况是因为该image仍旧被 ...

  8. Dubbo服务启动依赖检查

    dubbo 官方文档地址:http://dubbo.io/User+Guide-zh.htm 项目中存在服务之间存在循环依赖,启动时总是报错,通过修改启动检查check=false解决,下面是dubb ...

  9. OpenSSH免密码登录SSH2

    SSH2免密码登录OpenSSHhttp://blog.csdn.net/aquester/article/details/23836299 两个SSH2间免密码登录http://blog.csdn. ...

  10. 说说javap命令

    javap定义 javap是 Java class文件分解器,可以反编译(即对javac编译的文件进行反编译),也可以查看java编译器生成的字节码.用于分解class文件. 测试类 public c ...