引入
1 在开发的过程中,我们经常遇到某些耗时很长的javascript操作,并且伴随着大量的异步。
2 比如我们有一个ajax的操作,这个ajax从发出请求到接收响应需要5秒,在这5秒内我们可以运行其他代码段,当响应到达后,我们需要判断响应的结果(无非就是成功或者失败),并根据不同的结果 添加回调函数。
3 为了有效的简洁的添加回调函数jQuery引入了Callbacks。
4 而为了方便的 根据不同的结果(或者根据各种跟结果有关的逻辑,比如不管是成功或者失败) 添加回调函数,jQuery引入了Deferred。
$.ajax("test.html")
.done(function(){ alert("success"); })
.fail(function(){ alert("error"); });
5 因而Deferred与Callbacks是密不可分的,事实上,Callbacks也是从Deferred中分离出去的
回顾Callbacks
1 Callbacks大体架构

2 Callbacks源码分析:
源码讲解另外要注意下面两个参数:
once:如果创建Callbacks时加入该参数,则运行数组中的所有回调函数之后,也就是fire()之后,会清空数组。
memory:会保存上一次运行fire(args)时的参数args,每当添加一个新的回调函数到数组中,会立即使用args作为参数调用新加的函数一次
Deferred讲解:
Deferred大体架构:

1 先来看一看tuple数组:
var tuples = [
// action, add listener, listener list, final state
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],// 解决 操作成功 Callbacks对象 最终状态为解决
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], // 拒绝 操作失败 Callbacks对象 最终状态为拒绝
[ "notify", "progress", jQuery.Callbacks("memory") ] // 通知 操作进行中 Callbacks对象 最终状态无(操作进行中的最终状态就是操作完成,完成无非就是转变为上面两种 成功或者失败)
]
jQuery的设计理念是这样的:
1 deferred对象有三种执行状态----完成 失败 进行中。
2 每种状态对应一个Callbacks实例
3 如果执行状态是"完成"(resolved),deferred对象立刻调用done()方法指定的回调函数(也就是执行已完成状态对应的Callbacks实例的fire方法);如果执行状态是"失败",调用fail()方法指定的回调函数;如果执行状态是"进行中",则继续等待,或者调用progress()方法指定的回调函数。
2 promise对象和deferred对象
一个是deferred外部接口对象,一个是内部promise对象。
promise对象是一个受限的对象, 这就是所谓的受限制的deferred对象,因为相比deferred对象, promise对象没有resolve(With), reject(With), notify(With)这些能改变deferred对象状态并且执行callbacklist的方法了,只能是then、done、fali等方法。
3 done fail progress 方法 与 resolve reject notify方法
在上图中我们已经说明了前三个方法就是Callbacks中的add方法。后三个调用了Callbacks中的fireWith()方法。
所以,我们可以总结出:
1 三种状态各对应一个Callbacks实例
2 使用done 或fail 或progress时,实际上就是往各自对应的Callbacks实例中的list数组添加回调函数
3 使用resolve 或reject 或notify时,则就是运行各自对应Callbacks实例中的list数组中的回调函数
4 then方法
then方法创建了一个新的promise对象,then就是pipe,我们可以想象是一个管道。管道就是能 ‘承上启下’(更贴切的来说,在Deferred中的then只做了承上,仅仅是个人观点)
var a = $.Deferred();
a.then(function(val){
console.log(val); // 2
return val * 2
}).then(function(val){
console.log(val); // 4
});
a.resolve(2)
如案例所示,下一个回调对象都能取到上一个回调对象的值,这样一直可以叠加往后传递。
关于then可能看了非常迷糊,不要紧,上面说的是then的高级特性,平时我们基本不怎么使用的。
平时我们大部分是使用then来代替done以及fail:
$.when($.ajax( "test.php" )) .then(successFunction, failureFunction );
deferred源码分析
define([
"./core",
"./var/slice",
"./callbacks"
], function( jQuery, slice ) {
jQuery.extend({
Deferred: function( func ) {
// 创建一个tuples数组,一个promise对象,一个deferred对象,一个state变量
var tuples = [
// action, add listener, listener list, final state
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],// 解决 操作成功 Callbacks对象 最终状态为解决
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], // 拒绝 操作失败 Callbacks对象 最终状态为拒绝
[ "notify", "progress", jQuery.Callbacks("memory") ] // 通知 操作进行中 Callbacks对象 最终状态无(操作进行中的最终状态就是操作完成,完成无非就是转变为上面两种 成功或者失败)
],
state = "pending",
promise = {
state: function() { // 返回当前状态
return state;
},
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
then: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
return jQuery.Deferred(function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) {
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
// deferred[ done | fail | progress ] for forwarding actions to newDefer
deferred[ tuple[1] ](function() { // 注意,这里的deferred指的不是新Deferred对象中的deferred(也就是不是指的newDefer)
var returned = fn && fn.apply( this, arguments );
// 若returned是一个deferred对象,则为returned添加一个回调函数,这个回调函数运行后使newDefer能够接收到returned的fire()参数
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
newDefer[ tuple[ 0 ] + "With" ](
this === promise ? newDefer.promise() : this,
fn ? [ returned ] : arguments
);
}
});
});
fns = null;
}).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function( obj ) { // 若obj非null,则将obj与promise对象结合并返回,否则返回promise对象
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
deferred = {};
// Keep pipe for back-compat 和之前的版本兼容
promise.pipe = promise.then;
// Add list-specific methods
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ], // 这是一个Callbacks 对象
stateString = tuple[ 3 ]; // 用字符串表示的状态
// promise[ done | fail | progress ] = list.add
promise[ tuple[1] ] = list.add; // Callbacks对象的add函数
// Handle state 处理状态
if ( stateString ) {
list.add(function() {
// state = [ resolved | rejected ]
state = stateString; // Callbacks对象中的回调函数列表第一项:改变状态
// [ reject_list | resolve_list ].disable; progress_list.lock
},
tuples[ i ^ 1 ][ 2 ].disable, // 因为reject,resolve是对立的,当行为为reject,那么resolve的Callbacks就无用了,将其回调函数列表清空即可
tuples[ 2 ][ 2 ].lock ); // 当行为为reject或者resolve时,即"结果已确定",那么就不允许再调用 "操作进行中" 的Callbacks对象的fire()了
}
// 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;
});
// Make the deferred a promise 将promise合并至deferred
promise.promise( deferred );
// Call given func if any 在初始化完deferred对象后,会立即运行func函数,并把deferred作为第一个参数传入
if ( func ) {
func.call( deferred, deferred );
}
// All done!
return deferred;
},
// Deferred helper
when: function( subordinate /* , ..., subordinateN */ ) {
/* 大体思路:
* 若参数(参数必需是deferred对象)只有一个,则返回这个参数的promise对象
* 若参数有多个,则生成一个新的deferred对象,并返回deferred对象的promise对象
* 当所有参数的状态为完成时,使新deferred对象的状态变为完成,
* 若有一个参数的状态为失败,则使新deferred对象的状态变为失败
*
* */
var i = 0,
resolveValues = slice.call( arguments ),//slice是数组的slice方法,一般情况下:resolveValues就是一个由deferred组成的数组
length = resolveValues.length,
// the count of uncompleted subordinates 没有运行完成的deferred对象的数量
// 如果length长度不为1或者subordinate是一个deferred对象则,remaining=length,否则remaining为0
remaining = length !== 1 ||
( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,//运算符优先级: 逻辑与逻辑或 优先级高于 ? :
// the master Deferred.
// If resolveValues consist of only a single Deferred, just use that.
// 如果remaining为1,则使用subordinate(这是一个deferred对象)即可,否则创建一个新的deferred对象
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
// Update function for both resolve and progress values
updateFunc = function( i, contexts, values ) {
return function( value ) {
contexts[ i ] = this;
values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
if ( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !( --remaining ) ) {
deferred.resolveWith( contexts, values );
}
};
},
progressValues, progressContexts, resolveContexts;
// Add listeners to Deferred subordinates; treat others as resolved
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
resolveContexts = new Array( length );
// 迭代resolveValues中的每一个deferred对象,为其添加不同状态下的回调函数
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 we're not waiting on anything, resolve the master
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
return deferred.promise();
}
});
return jQuery;
});
