jQuery从1.5版本开始引入了Deferred对象。在jQuery中Deferred对象用来解决回调函数问题。

Deferred对象是非常cool的。比如:当你想将一个事件程序关联到一个鼠标点击,你就会为它分配的元素的onclick事件,如:button.onclick= MyHandler。这个控件也需要这个处理的逻辑的时候,比如:button.onclick= MyHandler2时。常规中这样是满足不了要求的,因为你只能在一个时间分配一个功能。原来可以使用 DOM的addEventListener()的功能,它允许您添加尽可能多的事件,这个问题最终得到解决。现在,类似的问题又出现了使用Ajax调用。这一次,因为Ajax的限制,只支持一个回调函数。 所以,再使用DOM的addEventListener()却不可能满足。所以引入了Deferred对象。

什么是Deferred?

Deferred是jQuery中对CommonJS的异步模型的实现,旨在提供通用Promises/A接口,简化异步编程难度。

在jQuery1.5之前ajax是这样来回调的:

1
2
3
4
5
$.ajax({
url: "/test.html",
success: successFunction,
error: errorFunction
});

而在jQuery>=1.5时,可以这样来实现:

1
2
3
4
$.ajax("test.html")
  .done(function(){ console.log("success"); })
  .fail(function(){ console.log("error"); })
.progress(function(){ console.log("progress"); });

如何再次增加方法呢?很简单:

1
2
3
4
5
6
7
$.ajax("test.html")
  .done(function(){ console.log("success1"); })
  .fail(function(){ console.log("error1"); })
.progress(function(){ console.log("progress1"); })
.done(function(){ console.log("success2"); })
  .fail(function(){ console.log("error2"); })
.progress(function(){ console.log("progress2"); });

源码

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
163
164
165
166
167
168
169
170
/**
* jQuery.Deferred()是可链式调用的工具对象
* 它能够注册回调函数到一个队列中,
* 依次调用该队列中的回调函数,
* 并且能够将调链中上一个函数(同步或者异步)的返回状态传递到下一个回调函数中.
*
*
* 理念来自于@see http://wiki.commonjs.org/wiki/Promises/A
* @since 1.5
*/
define([
"./core",
"./var/slice",
"./callbacks"
], function( jQuery, slice ) {
jQuery.extend({
Deferred: function( func ) {
// 集体申明一些变量
// resolve 代表已完成 调用done()
// reject 代表已失败 调用fail()
// notify 代表未完成 调用progress()
var tuples = [
// 动作, 添加的监听方法, 监听列表, 最终状态
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
// 默认状态
state = "pending",
promise = {
// 返回方法
state: function() {
return state;
},
// 该方法不管是否是成功或者失败 都会调用
// 其实就是在done()、fail()上都绑定了方法
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
// done()、fail()的简便写法
// .then(fnDone, fnFail, fnProgress)
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() {
var returned = fn && fn.apply( this, arguments );
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();
},
// 让这个deferred有一个promise对象
// 只要提供deferred对象就会有这个promise对象
// promise中不支持直接写法状态
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
deferred = {};
// 保持pipe属性 实现向后兼容
promise.pipe = promise.then;
// 增加具体列表方法
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
// promise[ done | fail | progress ] = list.add
promise[ tuple[1] ] = list.add;
// 处理状态
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;
});
// Make the deferred a promise
promise.promise( deferred );
// 如果给予了func 就调用
if ( func ) {
func.call( deferred, deferred );
}
// 返回deferred
return deferred;
},
// Deferred 工具
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
resolveValues = slice.call( arguments ),
length = resolveValues.length,
// the count of uncompleted subordinates
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
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 );
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;
});

总结

新引入的Deferreds,是一个强大的编写异步任务的方法。原来的重点是放在如何组织​​成一个单一的回调回调逻辑,而是现在你可以指定几个单独行动的回调函数,因为在上下文中,这些都会被执行,不担心这么多同步。编写异步代码与deferreds是很方便的。

参考文档