Deferred异步队列的总结:
jQuery.Deferred(func) {
(1)创建成功、失败、消息回调函数列表,设置初始状态为pending
var doneList = jQuery.Callbacks('once memory'),
failList = jQuery.Callbacks('once memory'),
progressList = jQuery.Callbacks('once memory'),
state = 'pending',
lists = {
resolve: doneList,
reject: failList,
notify: progressList
},
promise = {
done:doneList.add,
fail:failList.add,
progress: progressList.add
}
//方法deferred.done(), deferred.fail(), deferred.progress()分别用于添加成功回调函数,失败回调函数,消息回调函数到
对应的函数列表中。
//添加触发成功、失败、消息回调函数列表的方法
for(key in lists) {
deferred[key] = lists[key].fire;
deferred[key + 'With'] = lists[key].fireWith;
}
//这里变量lists可以减少实现的代码行数,是值得学习的技巧
//便捷方法deferred.then(doneCallbacks, failCallbacks, progressCallbacks)用于同时添加成功回调函数,失败回调函数和消息
回调函数到对应的回调函数列表中
then: function(doneCallbacks, failCallbacks, progressCallbacks) {
deferred.done(doneCallbacks).fail(failCallbacks).progress(progressCallbacks);
return this;
}
always: function() {
deferred.done.apply(deferred, arguments).fail.apply(deferred, arguments);
return this;
}
}
(2)创建异步队列的只读副本
deferred.promise()用于返回当前异步队列的只读副本,或为一个普通的javascript对象增加只读副本中的方法并返回。
只读副本只暴露了添加回调函数和判断状态的方法:
done(),fail(),progress(), then(),always(),state(),pipe()。
不包含触发执行和改变状态的方法:
resolve(),reject(),notify(),resolveWith(),rejectWith(),notifyWith()。
(3)创建异步队列
deferred = promise.promise({}),
key;
for(key in lists) {
deferred[key] = lists[key].fire;
deferred[key + 'With'] = lists[key].fireWith;
}
deferred.done(function() {
state = 'resolved';
}, failList.disabled, progressList.lock).fail(function() {
state = 'rejected';
},doneList.disabled, progressList.lock);
(4)如果传入了函数参数func,则调用
if(func) {
func.call(deferred, deferred);
}
jQuery规定,$.Deferred()可以接受一个函数名(注意,是函数名)作为参数,$.Deferred所生成的deferred
对象将作为这个函数的默认参数。
(5)返回异步队列 deferred;
例如1:
$.fn.animatePromise = function(prop, speed,callback) {
var element = this;
return $.Deferred(function(dtd) {
element.animate(prop, speed, function() {
dtd.resolve();
if(callback) {
callback.apply(this, arguments);
}
});
}).promise();
}
我的理解:先创建Deferred对象,然后执行里面传入的函数。然后可以使用$.when()同步化不同的动画
var fadeDiv1Out = $('.div1').animatePromise({opacity:0},5000, function() {
alert('before or after');
}),
fadeDiv2In = $('.div2').animatePromise({opacity:0},1000, function() {
alert('which is first');
});
$.when(fadeDiv2In,fadeDiv1Out).done(function() {
alert('both animate finished');
});
在when中有下列的源码
if(length > 1) {
for(;i < length; i++) {
if(arguments[1] && args[i].promise && jQuery.isFunction(args[i].promise)) {
<span style="color:#ff0000;">args[i].promise().then(resolveFun(i), deferred.reject, progressFunc(i));</span>
} else {
--count;
}
}
}
resolveFunc()称为成功状态监听函数,,在成功状态监听函数中,当子异步队列的方法resolve()或者resolveWith()被调用进入成功状态时,都把成功参数放入数组args中的对应位置,
并使计数器的count减一
上面的方法还可以扩展如下:
$.each(["slideDown", "slideUp", "slideToggle", "fadeIn", "fadeOut", "fadeToggle"], function(index, item){
$.fn[item + 'promise'] = function(prop, speed, callback) {
var element = this;
return $.Deferred(function(dtd) {
element[name](prop, speed, function() {
dtd.resolve();
if(callback) {
callback.apply(this, arguments);
}
});
}).promise();
}
}
})
例如2:
$.ajax("test.html")
.done(function(){ alert("哈哈,成功了!");} )
.fail(function(){ alert("出错啦!"); } )
.done(function(){ alert("第二个回调函数!");} );
ajax操作时,deferred对象会根据返回结果,自动改变自身的执行状态;但是在其他普通函数中,这个执行状态必须由程序员手动指定。即需要如上例子中的,dtd.resolve();
例如3:
function readData(){
$.ajax({ url:"test", dataType:"json" })
.done(function() {
//....
});
}
readData();
//...这里想添加一些后续处理,但程序将在Ajax回调前执行,所以无法达到预期目的
正确的代码:
function readData(){
return $.ajax({ url:"test", dataType:"json" })
.done(function() {
//....
});
}
readData().done(function () {
//...想添加的后续处理可以加在这里处理
});
因为是异步调用,所以$.ajax函数的返回值不代表返回结果,只是一个Deferred对象。
如果一个函数包含Ajax调用,那个这个函数必须将Ajax返回的异步对象作为自己的返回值,否则函数的调用者
无法保证后续代码的正常执行顺序。
参考文章:
http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html
http://ju.outofmemory.cn/entry/55151
http://www.cnblogs.com/lhb25/archive/2013/03/05/jquery-deferred-promise.html