由于1.7版本后$.Callbacks从Deferred中抽离出去了,目前版本的Deferred.js代码不过150行,而真正$.Deferred的实现只有100行左右,实现的逻辑是相当犀利的。
因为Callback被剥离出去后,整个Deferred就显得非常的精简,代码直接通过extend扩展到静态接口上,对于extend的继承这个东东,在之前就提及过jQuery如何处理内部jQuery与init相互引用this的问题,所以当jQuery.extend只有一个参数的时候,其实就是对jQuery静态方法的一个扩展。
jQuery.extend({
   Deferred:function(func){
        ...省略代码....
        return deferred
   },
   when:function(func){
      ...省略代码....
      return deferred.promise();
   }
})
我们来具体看看2个静态方法内部都干了些什么?
Deferred整体结构:右边代码所示。
Deferred就是一个简单的工厂方法,有两种方式使用:
var a = $.Deferred()
$.Deferred(function(){})
内部其实是严重依赖$.Callbacks对象,Callbacks就是用来储存deferred依赖的数据的。
因为done、fail、progress就是jQuery.Callbacks("once memory")所有对应的处理:
var list = jQuery.Callbacks("once memory")
promise['done'] = list.add;
deferred定义了一系列的接口,堪称一绝,100多行的代码,精练的有些过分。
Deferred方法内部建议了2个对象,一个是deferred外部接口对象,一个是内部promise对象。
promise对象解释是一个受限的对象, 这就是所谓的受限制的deferred对象,因为相比之前, 返回的deferred不再拥有resolve(With), reject(With), notify(With)这些能改变deferred对象状态并且执行callbacklist的方法了,只能是then、done、fali等方法。
其内部通过tuples数组,存储了所有的接口API,通过遍历把所有的接口一次都挂到内部promise与deferred对象上。
其中定义了done、fail以及progress这几个方法,其实就是Callbacks回调函数中的add方法,用与push外部的的数据,保存在队列上。
我们通过resolve、reject以及notify其实也就是处理Callbacks中的队列列表。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script src="http://code.jquery.com/jquery-latest.js"></script>
<title></title>
</head>
<body>
<script type="text/javascript">
// jQuery. Deferred主要处理:
// 显而易见Deferred是个工厂类,返回的是内部构建的deferred对象
// tuples 创建三个$.Callbacks对象,分别表示成功,失败,处理中三种状态
// 创建了一个promise对象,具有state、always、then、primise方法
// 扩展primise对象生成最终的Deferred对象,返回该对象
// primise对象就是一个受限对象,只读
var Deferred = function(func) {
var tuples = [
//1 动作
//2 侦听器
//3 最终状态
//后面的操作将是围绕这些接口处理
["resolve", "done", jQuery.Callbacks("once memory"), "resolved"],
["reject", "fail", jQuery.Callbacks("once memory"), "rejected"],
["notify", "progress", jQuery.Callbacks("memory")]
],
state = "pending",
//扩展的primise对象
promise = {
state: function() {},
always: function() {},
then: function( /* fnDone, fnFail, fnProgress */ ) {},
promise: function(obj) {}
},
deferred = {};
//定义管道风格的接口pipe
promise.pipe = promise.then;
//逐个添加所有的接口到deferred对象上
jQuery.each(tuples, function(i, tuple) {
deferred[tuple[0]] = function() {
deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments);
return this;
};
deferred[tuple[0] + "With"] = list.fireWith;
});
//转成成promise对象
promise.promise(deferred);
//如果传递的参数是函数,直接运行
if (func) {
func.call(deferred, deferred);
}
return deferred;
}
//when就是一个合集的处理
//可以收集多个异步操作,合并成功后处理
//同时也可以绑定Promise 对象的其它方法,如 defered.then
//所以when内部必须要创建一个deferred对象
var when = function(subordinate /* , ..., subordinateN */ ) {
var i = 0,
resolveValues = slice.call(arguments),
length = resolveValues.length,
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
updateFunc = function(i, contexts, values) {
return function(value) {};
},
progressValues, progressContexts, resolveContexts;
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;
}
}
}
return deferred.promise();
}
</script>
</body>
</html>