js333 > 计算机互联网 > 金沙js333娱乐场源码分析

原标题:金沙js333娱乐场源码分析

浏览次数:193 时间:2019-11-20

$.when(deferreds),.whendeferreds

作者:禅楼望月( )

作者:禅楼望月(http://www.cnblogs.com/yaoyinglong )

1 引子

上一篇博文中介绍的Deferred,它表示一个延迟对象。但是很多时候,我们需要在多个延迟对象(异步代码)都执行完后再去执行另一段代码,这种情况下,使用Deferred就行不通了,就需要使用这里的$.when。

[+]view code

var wait1=$.Deferred(),
  wait2=$.Deferred();

wait1.done(function () {
  console.log('wait1-success');
});
wait2.done(function () {
  console.log('wait2-success');
});

setTimeout(function () {
  wait1.resolve();
},1000);
setTimeout(function () {
  wait2.resolve();
},2000);

$.when(wait1,wait2)
    .done(function () {
      console.log('both wait1 and wait2 are success')
    });

[+]view code

var wait1=$.Deferred(),
    wait2=$.Deferred();

wait1.fail(function () {
    console.log('wait1-fail');
});
wait2.fail(function () {
    console.log('wait2-fail');
});

setTimeout(function () {
    wait1.reject();
},1000);
setTimeout(function () {
    wait2.reject();
},2000);

$.when(20,wait1,wait2)
    .fail(function () {
        console.log('fail');
    });

可以看出,要触发done,必须当所有的Deferred都触发resolve,而要触发fail,只要任意一个Deferred触发reject即可。$.when的用途就是来管理多个延迟对象,其他只传一个Deferred或者传递的不是延迟对象。都是没什么意义的,并不代表会出错哦。

1 引子

观察者模式是我们日常开发中经常用的模式。这个模式由两个主要部分组成:发布者和观察者。通过观察者模式,实现发布者和观察者的解耦。

发布者主要负责发布内容,观察者主要负责监听发布者发布的内容,并作出相应的动作。和我们平时订阅期刊一样,cnki会维护一个订阅者列表,有期刊被发布出来时,cnki会将这些期刊推送给订阅者。从程序角度来说,订阅者就是一堆的方法,发布者的推送内容的动作就是依次调用订阅者列表中的方法(订阅者),而发布的内容就将以参数的形式提供给订阅者。

金沙js333娱乐场 1

在jQuery中Deferred就是用来实现这一模式的。

我们首先来一个简单的订阅与发布示例:

[+]view code

 

//创建一个发布者

var subject=$.Deferred();

//创建两个订阅者

function subscriber1(content){

    console.log(content);

}

function subscriber2(content){

    console.log(content);

}

//订阅内容

subject.done(subscriber1,subscriber2);

//发布内容

subject.resolve('谢谢你的订阅');

金沙js333娱乐场 2

下面我们来分析,$.Deferred的源码,在这个过程中,我们还会$.Deferred的各种使用情况。

2 源码解析

在开始源码分析之前,我们先想一想,让我们自己实现这个$.when,应该怎么来实现。首先它肯定是个延迟对象,然后呢,它要等所有的Deferred都触发resolve它才触发resolve,所以需要一个计数器,计数器初始值为参数的个数,某个Deferred触发resolve时,我们就让计数器减1,某个Deferred触发reject时,我们就将计数器置零。然后调用相应的done或者fail,即可。那我们来看jQuery值怎么实现的。

2 构建Deferred

首先我们将Deferred源码做简化处理:

金沙js333娱乐场 3

其中,tuples是一种数据结构。它是一种非常巧妙的设计,是把有共性的代码合并到一起,然后一次性进行处理。promise是deferred的一种简化形式(去掉了改变状态的接口)。Deferred是一个工厂类,返回的是内部创建好的deferred对象。

[+]view code

 

var tuples =[
  // action, add listener, listener list, final state
  //once 表示resolve和reject只要触发过一次,后面就不能再被触发了
  ["resolve","done", jQuery.Callbacks("once memory"),"resolved"],
  ["reject","fail", jQuery.Callbacks("once memory"),"rejected"],
  ["notify","progress", jQuery.Callbacks("memory")]
]

tuples定义3中状态的,即resolve,reject,notify。这三种状态分别对应了3中订阅接口(done,fail,progress)和3种订阅列表(3个Callbacks),resolve和reject有最终的状态(resolved和rejected)。下面就是为找这个数据结构,来构建带这3中状态的Deferred。

首先定义了初始状态:state = "pending"

然后构建promise对象:

金沙js333娱乐场 4

在构建deferred对象:

金沙js333娱乐场 5

构建deferred分为2步:

1. 构建3种状态对应的接口,订阅者列表,和推送内容接口。

构建订阅者列表,3430行,列表由一个Callbacks来管理,其实整个Deferred就是基于Callbacks来实现的,把它们的代码放在一起对比,会发现出奇的相似,构建状态字符串(分别为resolved、rejected或者undefined)。

$.each执行完会有3个列表生成:

doneList=$.Callbacks("once memory");
failList=$.Callbacks("once memory");
progressList=jQuery.Callbacks("memory");

定义订阅接口3434行,他们就是订阅列表(Callbacks)的add方法。add进去的订阅者会存储在Callbacks的list数组中。

3中订阅接口分别为:

promise.done=doneList.add;
promise.fail=failList.add;
promise.progress=progressList.add

为resolved和rejected两种状态的列表添加预设的3个回调函数(3437~3444)。第一个回调函数用于处理Deferred的状态(已成功或已失败),第二个回调函数用于让另一个列表失效,这是因为当发布者发布内容的动作已经成功了,它就不可能再触发失败的状态,因此让失败列表失效,防止给正在订阅了失败状态的函数被执行。反之亦然。然后锁定notify列表。锁定notify只是让其的progressList的fire方法(即后面不能使用notify和notifyWith再进行推送内容了)不能被调用了。但是,我们还可以给notify状态继续添加订阅者,这些订阅者会立即被执行。

这里有个小技巧:i^1,^是按位运算符

金沙js333娱乐场 6

没什么大用,但是jQuery就是要连这点性能也要节省出来。

至此,doneList的list中有3个回调函数[changeState, failList.disable, progressList.lock],failList的list中同样有3个回调函数[changeState, doneList.disable, progressList.lock]

构建推送内容接口(3447~3451),三种接口名字分别为

deferred.resolve(成功),deferred.reject(失败),deferred.notify(正在执行),这三个接口在内部其实又去调用这三个接口对应的带参数版本(deferred.resolveWith,deferred.rejectWith,deferred.notifyWith),而三个接口又是订阅列表(Callbacks)的fireWith方法。由此可见,要能非常明白的看懂Deferred的源码,必须对Callbacks的源码非常熟悉,至少要能熟练运用Callbacks。

2. 将deferred的精简版promise中的方法和属性或从到自己身上。(3455)这里的promise也是一种非常巧妙的处理。

构造结束后我们就会得到如下图所示的Deferred对象:

金沙js333娱乐场 7

至此,Deferred对象构建完成。

比较发现,promise比deferred少了6个内容推送功能。没有了这几个功能deferred的状态也就不会被改变。

2.1 对象构建

逐行分析:

remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0

当只有一个参数,判断它是不是Deferred,不是的话让计数器为0。其他情况计数器都等于参数的个数。这里忽略了其他参数不为Deferred的情况,而是将处理放在后面进行。

deferred = remaining === 1 ? subordinate : jQuery.Deferred()

如果目前检测出只有一个Deferred时(这种情况就是when的参数只有一个,并且为Deferred),when内部就是用这个Deferred。就如我上面所说的,这样做就没有任何意义了。只是让JS引擎多跑了几个弯而已。如果有多个Deferred,则创建一个新的Deferred。

接下来创建了一个名为updateFunc的函数,它是在执行期执行的,这里跳过。

[+]view code

if ( length > 1 ) {
   progressValues = new Array( length );
   progressContexts = new Array( length );
   resolveContexts = new Array( length );
   for ( ; i < length; i++ ) {
      if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
         resolveValues[ i ].promise()
            .done( updateFunc( i, resolveContexts, resolveValues ) )
            .fail( deferred.reject )
            .progress( updateFunc( i, progressContexts, progressValues ) );
      } else {
         --remaining;
      }
   }
}

if判断when有没有参数传递进来,其实这里没必要判断length,判断remaining就可以了。在if中,使用遍历when的所有参数。发现有不是Deferred的,立即--remaining。如果参数是Deferred,则为该Deferred的3个状态(resolve,reject,notify)分别注册函数。注册的这3个函数就是用来当传递进来的这些Deferred有相关动作的时候,让when的Deferred得到通知(--remaining或者触发done或者立即调用fail)。

if ( !remaining ) {
   deferred.resolveWith( resolveContexts, resolveValues );
}

如果现在判断when的参数中没有Deferred是,直接触发when的resolveWith方法。因此就会触发done方法列表。

return deferred.promise();

返回一个精简版的Deferred。主要是为了防止在外部修改了when的Deferred的状态。

至此,when所对应的延迟对象构建成功。它是一个不能被修改状态的精简版Deferred。

3 Deferred的使用

2.2 执行期

执行期没什么内容,就是去执行upateFunc函数。

[+]view code

updateFunc = function( i, contexts, values ) {
   return function( value ) {
      contexts[ i ] = this;
      values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
      if( values === progressValues ) {
         deferred.notifyWith( contexts, values );
      } else if ( !( --remaining ) ) {
         deferred.resolveWith( contexts, values );
      }
   };
}

在前面构建期,为参数Deferred添加注册时间时,done,progress是这样的:

done( updateFunc( i, resolveContexts, resolveValues ) )

progress( updateFunc( i, progressContexts, progressValues ) )

然后upateFunc返回一个闭包。闭包中,首先配置values,配置触发resolveWith触发时的参数,这个参数会传递给每一个订阅者。然后判断,如果是progress传递进来的方法,则为触发的是notifyWith。计数器,不做变化,只是调用通知订阅者列表。否则,则先让计数器减减,再判断计数器是否为0,是则触发resolveWith方法。这里的deferred指的是when的Deferred。不能混淆了。

那如果,when的参数中有一个触发了reject呢?就会直接调用deferred的reject方法。

fail( deferred.reject )

因为这里给when的每个Deferred参数的reject添加了一个这样的订阅方法:立即出发deferred的reject方法。

总体来说,when的源码还是比较简单的。

作者:禅楼望月( ) 1 引子 上一篇博文中介绍的Deferred,它表示一个延迟对象。但是...

3.1 done,resolve; fail,reject; progress,notify

[+]view code

var def=$.Deferred();
setTimeout(function () {
console.log('耗时操作……');
def.resolve('本次推送的内容……');
},1000);
def.done(function () {
console.log(arguments[0]);
});

金沙js333娱乐场 8

这个过程非常简单,只是对Callbacks的简单包装。

 

[+]view code

var def=$.Deferred();
setTimeout(function () {
  new Error('不好意思,出错了……');
  def.reject('台风影响,本次推送出错……');
},1000);
def.done(function () {
  console.log(arguments[0]);
}).fail(function () {
  console.log(arguments[0]);
});

这里的原理和是那个面的一模一样。

 

[+]view code

var def=$.Deferred();
setTimeout(function() {
    console.log('正在执行……');
    def.notify('正在执行……');
},1000);
def.done(function () {
    console.log(arguments[0]);
}).fail(function () {
    console.log(arguments[0]);
}).progress(function() {
    console.log(arguments[0]);
});

3.2 then()

[+]view code

var def=$.Deferred();
setTimeout(function () {
    console.log('正在执行……');
    def.notify('正在执行……');
},1000);
def.then(function () {
    console.log(arguments[0]);
},function () {
    console.log(arguments[0]);
},function () {
    console.log(arguments[0]);
});

金沙js333娱乐场 9

then方法返回一个新的的Deferred对象的精简版promise。在创建该Deferred是传递进去了一个function参数。我们发现,在构建Deferred的工厂方法就有一个func参数,它内部的处理是这样的:

金沙js333娱乐场 10

以工厂方法创建的deferred为上下文,以该deferred为参数来调用这个方法。函数执行完后,工厂方法将deferred返回。

然后我们来看这个func具体是怎么设计的,这里的设计的非常之复杂。这里最重要的概念就是作用域链,如果对这些基础知识不熟悉的话,理解起来真的很费劲……

金沙js333娱乐场 11

以上面实例代码为例,来分析then的源码:

金沙js333娱乐场 12

分析源码不仅可以回顾JavaScript知识,还可以借鉴别人的设计思路,代码书写方式,对提高自身的修养是很有帮助的。

下一章分析jQuery.when的实现……

本文由js333发布于计算机互联网,转载请注明出处:金沙js333娱乐场源码分析

关键词:

上一篇:Css背景渐变,css3渐变详解

下一篇:没有了