一,deferred的使用和基本原理
具体的用法可以参见阮一峰的一篇日志:
http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html
简单的说,deferred对象就是一个异步回调队列。
deferred对象最大的作用就是用在ajax中实现异步回调,
在没有deferred对象是,ajax必须这样写:
$.ajax({
url: 'xxx',
success: function(){}
error: function() {}
})
这样的写法非常死板,即不方便定义多个回调,也不能写成链式操作。
在1.5版本引入deferred对象之后,ajax被用deferred改写,ajax方法返回的不再是ajax对象,而是deferred对象,所以可以实现这样的写法:
$.ajax('/xxx').success(function(){}).fail(function(){})
deferred的基本原理就是:
它定义了两种基本状态:resolved和rejected,然后定义了三个方法success,fail,always来定义当deferred对象从初始状态转换到两种基本状态时候应该调用哪些函数。最后提供了reject/rejectWith,resolve/resolveWith来改变deferred对象的状态,以便出发对应的回调函数
deferred对象的用途就是对那些非常费时的操作定义回调函数
二,deferred对象的实现
deferred对象的实现会依赖callback对象来管理回调函数队列。
先说下callback,他就是一个回调函数队列,提供add方法想队列里面添加内容,然后提供fire/fireWith对象来调用回调函数。
所以,deferred的实现比较简单
下面对源码逐段解析
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") ]
],
首先定义tuples数组,方便后面引用。
var promise={}
然后定义了一个promise对象
注意promise和deferred对象的区别:
promise方法已经被混入deferred对象了,但是deferred对象的一些方法没有在promise中,主要就是触发状态转换和函数回调的几个方法比如reject/rejectWith。
所以promise能做的 deferred都能做到,但是deferred能触发状态转换确实promise做不到的。所以使用promise方法返回promise对象可以防止被其他人改变自己的状态,他们只能通过promise来注册回调,但是不能通过promise来改变状态。
只是需要注意其中的两个方法:
then: 返回的是promise对象,然后默认把三个参数分别作为resolve, reject, notify状态的回调,所以这个方法就可以当做是需要连续定义三种回调的一个快捷方式
promise: 返回promise对象
deferred = {};
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
// promise[ done | fail | progress ] = list.add
promise[ tuple[1] ] = list.add;
// Handle state
if ( stateString ) {
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 );
}
// deferred[ resolve | reject | notify ]
deferred[ tuple[0] ] = function() {
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
return this;
};
deferred[ tuple[0] + "With" ] = list.fireWith;
});
然后就定义了一个deferred对象,并添加了很多方法,因为是通过遍历tuple来逐个添加的,可以以tuples[0]为例
tuples[0]= [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ]
首先定义了 list = jQuery.Callbacks("once memory")也就是一个回调队列
然后 promise['resolve'] = list.add,所以直接调用promise.resolve(fun)就是把这个函数添加到resolved队列中
下面是就是加锁操作
在下面,定义了 promise.resolveWith = list.fireWidth, 而 promise.resolve 就是 promise.resolveWidth 就是调用promise.resolve 但是this设了默认值。
整个deferred对象的构造是非常简单的,基本就是调用callback来实现存储和调用回调队列,然后引用下callback的部分函数
需要注意一点就是下面的代码
Deferred: function( func ) {
……
if ( func ) {
func.call( deferred, deferred );
}
// All done!
return deferred;
}
所以说你如果这样调用 $.Deferred(fun),那么会调用fun并传入一个deferred对象,如果没有fun,则直接返回deferred对象。