add = function( args ) {
var i,
length,
elem,
type,
actual;
for ( i = 0, length = args.length; i < length; i++ ) {
elem = args[ i ];
type = jQuery.type( elem );
if ( type === "array" ) {
// Inspect recursively
add( elem );
} else if ( type === "function" ) {
// Add if not in unique mode and callback is not in
if ( !flags.unique || !self.has( elem ) ) {
list.push( elem );
}
}
}
},
2)如果没有在执行,并且memory既不是undefined也不是true,则表示曾经执行过,那么更改firingStart为之前的list.length,这样这一次就会只执行刚添加进去的方法,同时立即调用fire方法,再把刚刚添加进去的方法立即执行, 通过参数可以看出来,刚才memory那个数组就是保存context和参数。
// With memory, if we're not firing then
// we should call right away, unless previous
// firing was halted (stopOnFalse)
add: function() {
if ( list ) {
var length = list.length;
add( arguments );
if ( firing ) {
firingLength = list.length;
} else if ( memory && memory !== true ) {
firingStart = length;
fire( memory[ 0 ], memory[ 1 ] );
}
}
return this;
},
chapter3 Deferred Object 异步队列
jQuery(1.5以后)的ajax这类异步任务都被重写,将ajax与回调函数解耦,实现高内聚低耦合。
jQuery的异步队列部分也比较简洁,代码主要集中在1040-1400之中, 主要实现jQuery.Callbacks 和 jQuery.Deferred与jQuery.when三个方法。
jQuery.Callbacks又是在1.7引入,返回一个链式工具对象,用于管理所有回调函数。 后两个是通过jQuery.extends添加上去,因此我们从jQuery.Callbacks开始,看jQuery的Deferred Object异步队列都做了什么,又是怎么实现的
jQuery.Callbacks
先来看整个结构 jQuery.Callbacks定义了一堆变量 + 两个方法 add、fire与一个对象self,self内部属性也是一堆方法,最终整个函数以self作为返回值。
createFlags
add
这个方法比较简单,可以看出来就是将args数组内部元素一个个添加进list, list也是jQuery.callbacks内的一个数组元素。 如果args内部还有数组则递归解构, 同时,也会根据flag的标签来检测是不是重复性。
fire
使用指定的上下文和参数来执行list中的回调函数 这里有一个变量特别乱 memory, 它的初始值是undefined, flags.memory表示在建callbacks时是否传入了memory这个flag。
即是看完这段代码也对memory有点似懂非懂,再加上最后once这个flag。 不过可以看出来,是通过这几个flag来实现代码逻辑的分流操作。 基本可以看出来,只要list回调函数执行过之后,memory就被变成了[ , ]或true
self.add()
这个add大概可以帮助我们理解刚才上面那个fire函数内部复杂的逻辑。
首先调用add()将要添加的方法添加进list,
1)紧接着检测当前回调函数式是不是在执行过程,如果在执行过程,就把firingLength重新计算一下,这样就会把刚刚添加进去的方法给执行了。
2)如果没有在执行,并且memory既不是undefined也不是true,则表示曾经执行过,那么更改firingStart为之前的list.length,这样这一次就会只执行刚添加进去的方法,同时立即调用fire方法,再把刚刚添加进去的方法立即执行, 通过参数可以看出来,刚才memory那个数组就是保存context和参数。 // With memory, if we're not firing then // we should call right away, unless previous // firing was halted (stopOnFalse)
self.remove()
我们发现add和remove都有个前提就是list存在,而且不存在的话不会使用默认的[],这表明,如果list不存在了,就代表执行过了,例如有once flag的情况, 表示只执行一次回调函数
remove对应于add表示要移除某些回调函数。
self.has、empty、disable、disabled、lock、locked
self.fireWith、fire、fired
jQuery.Deferred
借助Callbacks 我们来看一下jQuery.Deferred是如何实现的, Deferred是jquery中很重要的一个延迟解决方案,它和promise思想很类似,也是jQuery.ajax的重要基础。因此,deferred就是jQuery的回调函数解决方案
我们都知道promise有一个很重要的特征就是有一个状态,初始值是pending,成功后变为resolved, 失败后变为rejected, 并且这个状态是单向不可逆的,同时只能更改一次,也就是说也么从pending-->resolved, 要么从pending-->rejected。 然后对应的会执行成功,或者失败的回调函数。
带着这个基础再来看deferred的实现。
这段代码看起来也不是很复杂,维护了三个回调函数队列和一个state,根据不同的方法去执行相对应的回调函数队列,并维护好state。一切的基础都在Callbacks。 可能有点不太明白的就是为什么要先声明一个promise再赋给deferred, 感觉直接声明deferred好像也没什么不可以。
when
when这段代码更容易理解。 when就是接受多个deferred对象,当所有都resolved之后,认为整个when是成功的,执行成功回调函数,否则,任何一个deferred参数fail了,则整个when是fail的。
Summary
deferred部分在jQuery中起到了函数异步管理的作用,但内部并没有用到任何异步的内容,它的基础是jQuery.Callbacks对象。
jQuery.Callbacks对象:它在内部维护一个管理函数的list,同时有一个很重要的firing状态用来表示当前list内的函数是不是在执行中,对list的任何操作都需要考虑firing状态,用于保证list内的函数执行过程中即不略过也不被重复执行。
deferred也是一个很有意思的内容,deferred内部维护三个jQuery.Callbacks对象的list,对应done、fail、progress三个状态。并且deferred的三个状态是模拟promise的状态,逻辑处理也是按照promise的逻辑来处理,状态只能更改一次。
when是一个多deferred管理的方法,接受多个deferred作为参数,但实现一个主deferred对象同时when.count变量作为整体状态的记录,并且为每个deferred添加一个done和fail方法,done用于一直维护when.count, 然后fail用于终止并调用主deferred的fail。
这三个对象的设计可谓精妙。让使用者在主观上完成了异步、回调函数的轻松管理,再加上其链式写法让整个回调的管理更加简单。 但研究它的源码,也不过区区一两百行,但实现的东西却是极为丰富的。