jQuery源码阅读12-2:回调队列$.Deferred对象

Deferred这个东西我没看懂… 只看出他能干嘛了 连他实现的思路都没研究清楚…
后边很多模块的核心都是Deferred(比如Ajax)本应好好研究研究的, 不过时间有限, 先把读代码笔记发出来吧 回头再迭代

deferred对象有三种执行状态:未完成,已完成和已失败.
如果执行状态是”已完成”(resolved),deferred对象立刻调用done()方法指定的回调函数;如果执行状态是”已失败”,调用fail()方法指定的回调函数;如果执行状态是”未完成”,则继续等待,或者调用progress()方法指定的回调函数

一:用法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//给本地普通指定回调
var wait = function(dtd) {    
var dtd = $.Deferred(); //在函数内部,新建一个Deferred对象
    
var tasks = function() {      
alert("执行完毕!");      
dtd.resolve(); // 改变deferred对象的执行状态,将dtd对象的执行状态从"未完成"改为"已完成",触发done()方法
//deferred.reject()//将dtd对象的执行状态从"未完成"改为"已失败",从而触发fail()方法   
};
  
setTimeout(tasks, 5000);    
return dtd.promise(); // 返回promise对象
};  
//之后用when回调,when只对Deferred实例进行操作
$.when(wait())
.done(function() {
alert("哈哈,成功了!");
})
.fail(function() {
alert("出错啦!");
});

二:源码阅读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/**
* 博文作者:ruantao1989{@}gmail.com
* 引自博客:ruantao.duapp.com/blog
*/
jQuery.extend({
Deferred: function( func ) {
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[0][0至1][3],共3种"pending初始","resolved已解决","rejected被拒绝"
state = "pending",
promise = {
//把实例的state改为只读
state: function() {
return state;
},
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
//核心, 用闭包返回一个jQuery.Deferred实例
then: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
return jQuery.Deferred(function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) {
var action = tuple[ 0 ],
fn = fns[ i ];
// deferred[ done | fail | progress ] for forwarding actions to newDefer
deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
function() {
var returned = fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
}
} :
newDefer[ action ]
);
});
fns = null;
}).promise();
},
//==>源码1147行
promise: function( obj ) {
//如果传入一个对象(deferred),则把promise对象合并到deferred
//否则就返回promise对象
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
//初始化deferred对象为空
deferred = {};
//先前兼容pipe方法
promise.pipe = promise.then;
//第一步:在这遍历添加方法
//tuples就3个元素,每个元素是一个数组,对应[执行函数, 回调函数名, 监听列表 , 运行后状态]
jQuery.each( tuples, function( i, tuple ) {
//Callbacks("once memory")所返回的Callbacks对象
var list = tuple[ 2 ],
//状态字段
stateString = tuple[ 3 ];
//tuple[1]是done,fail,progress中的一个
// promise["done"或"fail"或"progress"] = Callbacks.add
promise[ tuple[1] ] = list.add;
//只有最后一组没有状态位,不添加
if ( stateString ) {
//Callbacks.add()接收多个参数, 会遍历并添加进回调列表
list.add(function() {
//1.state = "resolved"或"rejected"
state = stateString;
// [ reject_list | resolve_list ].disable; progress_list.lock
//[ i ^ 1 ]按位异或: 0,1,2对应1,0,3, 其中2的情况之前配排除了
//所以"已解决"或"被拒绝"两种状态,会禁用当前所处的另一种,并锁定"notify"的回调队列
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
}
//deferred的["resolve"或"reject"或"notify"] = $.Callbacks.fire
deferred[ tuple[0] ] = list.fire;
//deferred的["resolveWith"或"rejectWith"或"notifyWith"] = $.Callbacks.fireWith
deferred[ tuple[0] + "With" ] = list.fireWith;
});
//第二步:promise对象的promise方法(源码1147行)传入第一步遍历增加callback队列的deferred对象
promise.promise( deferred );
//$.Deferred(接收的参数),为了支持示例中$.Deferred(wait)的调用方法
if ( func ) {
func.call( deferred, deferred );
}
// All done!
return deferred;
},
// Deferred helper
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
//参数数组副本
resolveValues = core_slice.call( arguments ),
//参数个数
length = resolveValues.length,
//传入的要是$.Deferred实例,就有promise,length就是Deferred实例个数(一个或多个)
//否则remaining就是0
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
//传入一个参数的话, 就不用生成新的jQuery.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 ? core_slice.call( arguments ) : value;
//其实都是用fireWith触发
if( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !( --remaining ) ) {
deferred.resolveWith( contexts, values );
}
};
},
progressValues, progressContexts, resolveContexts;
//length > 1就是有多个Deferred实例
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;
}
}
}
//remaining要是等于0了,就应该没有要处理的了,就调用deferred.resolveWith
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
return deferred.promise();
}
});