这一课,我们先接着上一课讲一下wait方法,以及wait方法是如何从静态方法变化实例方法的。

首先我们先看wait方法为啥可以从静态方法变成实例方法,请看register源码:

Deferred.register= function(name, fun){  //name="wait",fun=Deferred.wait;

  this.prototype[name] = function(){   //this=Deferred,this.prototype[name]  = Deferred.prototype.wait。

    var a = arguments;

    return this.next(function(){  return fun.apply(this,a)}  )

  };

};

然后我们来看wait方法为啥可以延迟执行fun2函数?

我们上一课的例子:Deferred.next(function fun1(){alert(1)}).wait(1).next(function fun2(){alert(2)}); 这里调用的wait方法其实是:

function(){

    var a = arguments;

    return this.next( function(){  return fun.apply(this,a)     } )     //fun = Deferred.wait,this指的是第一个next方法(静态方法)返回的Deferred实例d1

};

因为你传入了1,所以a=1,然后执行this.next方法,因为这时的this是实例对象,因此调用实例对象的next方法。这时就会把function(){  return fun.apply(this,a)},添加给d1._next(Deferred实例对象d2)的callback.ok属性。返回d2。接着执行d2的next方法,返回d2._next(d3)。这时候,等静态方法next的定时器结束后(0秒后,实际是几毫秒后,当然每个浏览器不一样),执行队列上就会执行fun1方法,执行结束后,就会立即执行function(){  return fun.apply(this,a)     },也就是执行Deferred.wait.apply(this,1),this是d2。

Deferred.wait = function(n){

  var d= new Deferred() , t = new Date();    //这里又新建一个Deferred,我们称为d4.

  var id = setTimeout(function(){

    d.call((new Date()).getTime() - t.getTime());

  }, n * 1000);    //延迟一秒后,再执行d4.call(1000);这里的d4没有设置callback.ok属性值,因此使用它的默认值:Deferred.ok = function(x) { return x};因为这里延迟一秒后才执行,所以会执行下面的代码,也就是返回d4.这时候,你在_fire里面就可以看到,当next中添加的回调函数返回Deferred对象时,会执行这个操作value._next = this._next;也就是把 d4._next = d2._next。因此过了一秒后,执行d4的callback.ok方法,这时返回的值是1000,就会继续执行d4._next(d3)的回调函数fun2。

  d.canceller = function(){

    clearTimeout(id);

  }

  return d;

}

Deferred.register("wait",Deferred.wait);  //这里就是把静态方法wait变成实例方法wait的。

因此,wait方法,可以延迟执行wait方法后面的next中添加的回调函数,非常有用,我们可以像时钟那样,想过多久执行这个函数,就过多久执行。

接下来,我们来看一下JSDeferred的并归结果:

JSDeferred最厉害之处,是它的parallel的实现。这个parallel是来解决这类问题的:有一个业务,需要发起4个Ajax请求,这4个请求的地址和返回时间不一样,必须等到它们都处理完成后,整合它们4个的返回数据,然后根据这个整合的数据再发起两个ajax请求,等到这两个ajax请求全部处理完成,整合这两个请求的数据后,再进行一次ajax请求,返回数据才算一次业务处理成功。

我们先来看一下它是如何使用的:

Deferred.parallel(function(){ return 1 },function(){ return 2 },function(){ return 3 }).next(  function(a){ console.log(a);  //[1,2,3]  }  )

上面这段代码的意思是:先执行第一个匿名函数,执行完后,再执行第二个匿名函数,接着再执行第三个匿名函数,等第三个执行完后,才会执行next中添加的匿名函数,而且可以接收到parallel中匿名函数返回的值。

我们再弄一个我们前端开发中经常会使用的操作:

function ajax(url){   //模拟ajax请求

  var d = new Deferred();

  var delay = url.match(/time=(\d+)/)[1];   // 此正则/time=(\d+)/,会取到字符串time=2000,因为match方法,所以会返回一个数组,数组第一项是匹配的整个字符串,也就是"time=2000",数组的第二项就是第一个子表达式取到的值,这里是2000(通过\d+取到的),因此delay = "2000"

  setTimeout(function(){   //这里模拟ajax请求,也就是2秒后ajax请求结束,执行回调函数。

    d.call(delay);     //告诉parallel,我执行完了。只有等三个ajax方法,都执行完了后,才会执行next中添加的回调函数。

  },+delay)

  return d;   //当第一个ajax方法执行到这里时,就会继续执行第二个ajax方法(不会等2秒后,才执行第二个ajax方法)。

}

Deferred.parallel(

  ajax("chaojidan?time=2000"),     //ajax请求1,花费2秒

  ajax("chaojidan?time=3000"),    //ajax请求2,花费3秒

  ajax("chaojidan?time=4000")   //ajax请求3,花费4秒

).next(function(a){

  console.log(a);    //   a=[2000,3000,4000]

});

上面代码执行的顺序,先执行第一个ajax方法,但是ajax请求是需要时间的,那么第二个ajax方法会等到第一个ajax请求结束后(也就是ajax的请求完成,readyState等于4的时候),再执行(这时执行完parallel需要9秒的时间),还是等第一个ajax方法return后(也就是ajax请求刚开始,readyState=2,貌似opera浏览器没有状态2,这里忽略),就执行(这时执行完parallel需要4秒时间,取三个ajax请求时间花费最多的值)。毫无疑问,这里讲的是异步编程,所以parallel执行完需要4秒时间,三个ajax请求互不影响,但是必须等这三个ajax请求都结束后,才会执行next中添加的回调函数。这很容易实现上面的那个业务需求。

最后,我们来看parallel方法的源码解析:

Deferred.parallel = function(d2){

  var isArray = false;

  if(arguments.length > 1){    //如果参数大于1,就把参数弄成数组形式,我们的例子就是执行这里

    d2 = Array.prototype.slice.call(arguments);

    isArray = true;

  }

  else if(Array.isArray && Array.isArray(d2) ){  //如果传入的本身是一个数组

    isArray = true;

  }

  var ret = new Deferred(),

    values = {},

      num = 0;

  for(var i in d2){    //数组也可以使用for in方式来遍历

    if(d2.hasOwnProperty(i)){   //数组有属性值0,1,2...

      (function(d,i){    //第一个例子,这里的d就是function(){return 1},第二个例子,这里的d就是new出来的Deferred实例对象
        if(typeof d == "function"){

          d2[i] = d = Deferred.next(d);  // 如果是函数,就转换成Deferred对象。这里调用的是静态方法next。因此过了浏览器的最小时钟间隔,就会马上执行d.next方法添加的回调函数。

        }

        d.next(function(v){    //如果传入的是Deferred实例对象d5,那么必须等到d5调用call方法后,才会执行这里next添加的回调函数。

          values[i] = v;

          if(--num <=0){    //只有等数组中的函数都执行完毕后,或者数组中的Deferred实例对象都调用了call方法后,就会进入if语句,执行ret.call。

            if(isArray){

              values.length = d2.length;   //给json对象values添加length属性。把普通json转换成特殊json。

              values = Array.prototype.slice.call(values,0);    //特殊的json对象,可以通过数组的slice方法,转换成数组

            }

            ret.call(values);

          }

        });

        num++;    //根据上面的例子,这里的num最终会变成3。

      })(d2[i],i);

    }

  }

  if(!num){   //如果parallel方法中没有传入参数,那么就直接执行ret.call();

    Deferred.next(function(){     //因为调用的是Deferred静态的next方法,因此过了浏览器的最小时钟间隔,就会立刻执行ret.call方法

      ret.call();

    })

  }

  return ret;   //返回回去的ret对象,它的._next = d4(parallel方法后面的next方法创建的Deferred实例对象),因此只要调用了ret的call方法,就会执行next中添加的回调函数。

}

如果上一课没看懂,这一课基本上就是挂掉的,有点复杂,初学者莫看。

加油!

第三十一课:JSDeferred详解2的更多相关文章

  1. “全栈2019”Java第三十章:数组详解(下篇)

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  2. Hadoop集群搭建安装过程(三)(图文详解---尽情点击!!!)

    Hadoop集群搭建安装过程(三)(图文详解---尽情点击!!!) 一.JDK的安装 安装位置都在同一位置(/usr/tools/jdk1.8.0_73) jdk的安装在克隆三台机器的时候可以提前安装 ...

  3. NeHe OpenGL教程 第三十一课:加载模型

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  4. Linux防火墙iptables学习笔记(三)iptables命令详解和举例[转载]

     Linux防火墙iptables学习笔记(三)iptables命令详解和举例 2008-10-16 23:45:46 转载 网上看到这个配置讲解得还比较易懂,就转过来了,大家一起看下,希望对您工作能 ...

  5. spring事务详解(三)源码详解

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  6. python selenium 三种等待方式详解[转]

    python selenium 三种等待方式详解   引言: 当你觉得你的定位没有问题,但是却直接报了元素不可见,那你就可以考虑是不是因为程序运行太快或者页面加载太慢造成了元素不可见,那就必须要加等待 ...

  7. python第三十一课--递归(2.遍历某个路径下面的所有内容)

    需求:遍历某个路径下面的所有内容(文件和目录,多层级的) import os #自定义函数(递归函数):遍历目录层级(多级) def printDirs(path): dirs=os.listdir( ...

  8. Window下PHP三种运行方式图文详解,window下的php是不是单进程的?

    Window下PHP三种运行方式图文详解,window下的php是不是单进程的? PHP运行目前为止主要有三种方式: a.以模块加载的方式运行,初学者可能不容易理解,其实就是将PHP集成到Apache ...

  9. OSGi 系列(三)之 bundle 详解

    OSGi 系列(三)之 bundle 详解 1. 什么是 bundle bundle 是以 jar 包形式存在的一个模块化物理单元,里面包含了代码,资源文件和元数据(metadata),并且 jar ...

  10. “全栈2019”Java第一百零三章:匿名内部类详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

随机推荐

  1. C++ sort函数

    (一)为什么要用c++标准库里的排序函数 Sort()函数是c++一种排序方法之一,学会了这种方法也打消我学习c++以来使用的冒泡排序和选择排序所带来的执行效率不高的问题!因为它使用的排序方法是类似于 ...

  2. hadooop2.6 job pending research

    https://hadoop.apache.org/docs/r2.6.0/hadoop-project-dist/hadoop-common/ClusterSetup.html 我使用的是已经运行在 ...

  3. HDU 5047 推公式+别样输出

    题意:给n个‘M'形,问最多能把平面分成多少区域 解法:推公式 : f(n) = 4n(4n+1)/2 - 9n + 1 = (8n+1)(n-1)+2 前面部分有可能超long long,所以要转化 ...

  4. [推荐] BC/Beyond Compare(差异比较软件)

    Beyond Compare 前一段时间,介绍过用Total Commander来完成文件夹同步的时候,一位朋友留言推荐了Beyond Compare--一个强大的超越了文件差异比较的工具.Beyon ...

  5. JMeter学习(十八)JMeter测试Java(二)

    实例: 服务为:将输入的两个参数通过IO存入文件: 1.打开MyEclipse,编写Java代码 服务: package test; import java.io.File; import java. ...

  6. UMLUnified Modeling Language (UML)又称统一建模语言或标准建模语言

    1.用例图(use case diagram) 2.活动图(activity diagram) 3.静态结构图 4.顺序图(Sequence Diagram):时序图 5.交互纵览图(Interact ...

  7. java14-9 Doteformat的练习

    需求: 键盘录入出生年月日,计算出距离现在已经生活了几天 分析: A:创建键盘录入固定模式的字符串 B:计算步骤: a:把输入进来的字符串格式化成日期 b:获取现在的日期,减去格式化后的日期 c:把得 ...

  8. 转: Github访问慢解决办法

    from: https://yq.aliyun.com/articles/36744 Github访问慢解决办法   zxiaofan 2016-04-20 17:25:00 浏览2156 评论0 摘 ...

  9. 转: 使用virtualenv搭建独立的Python环境

    转自:  http://qicheng0211.blog.51cto.com/3958621/1561685 谢谢作者的辛苦付出 virtualenv可以搭建虚拟且独立的python环境,可以使每个项 ...

  10. android应用中去掉标题栏的方法

    现在我坚定的认为写技术博客对自己有很大的帮助,写博客给自己一个学而思的机会. 在Android中去掉标题栏有三种方法,它们也有各自的特点. 1.在代码里实现 this.requestWindowFea ...