今天在项目中遇到了Deferred()的方法,虽然项目中使用的是dojo,但是其实各个框架都是相通的,以前都没有遇到过,所以今天就在网上找了些资料学习了一下:
定义和用法:
$.Deferred() 是一个构造函数,用来返回一个链式实用对象方法来注册多个回调,并且调用回调队列,传递任何同步或异步功能成功或失败的状态。
提示:
- $.Deferred() 构造函数创建一个新的 Deferred(延迟)对象, jQuery.Deferred 可传递一个可选的函数,该函数在构造方法返回之前被调用并传递一个新的 Deferred 对象作为函数的第一个参数。例如被调用的函数可以使用 deferred.then()来附加回调函数。
- 一个 Deferred 对象开始于挂起状态。任何使用 deferred.then(), deferred.always(), deferred.done(), 或者 deferred.fail() 添加到这个对象的回调函数都是排队等待执行的。调用 deferred.resolve() 或 eferred.resolveWith() 转换延迟到解决状态后立即执行设置的 doneCallbacks 。调用 deferred.reject() 或 deferred.rejectWith() 转换延迟到拒绝状态后立即执行设置的 failCallbacks 。一旦对象已经进入了解决或拒绝状态,它保持该状态。回调仍然可以添加到已解决或已拒绝的 Deferred 对象——它们会立即执行。
语法
$.Deferred( [beforeStart ] )
$.ajax('data/url')
.done(function(response, statusText, jqXHR){
console.log(statusText);
})
.fail(function(jqXHR, statusText, error){
console.log(statusText);
})
,always(function(){
console.log('I will always done.');
});
1.done,fail,progress都是给回调列表添加回调,因为jQuery的Deferred内部使用了其$.Callbacks对象,并且增加了memory的标记(详情请查看我的这篇文章jQuery1.9.1源码分析–Callbacks对象),
所以如果我们第一次触发了相应的回调列表的回调即调用了resolve,resolveWith,reject,rejectWith或者notify,notifyWith这些相应的方法,当我们再次给该回调列表添加回调时,就会立刻触发该回调了,
即使用了done,fail,progress这些方法,而不需要我们手动触发。jQuery的ajax会在请求完成后就会触发相应的回调列表。所以我们后面的链式操作的注册回调有可能是已经触发了回调列表才添加的,所以它们就会立刻被执行。
2.always方法则是不管成功还是失败都会执行该回调。
3.then方法会返回一个新的Deferred对象
- 如果then方法的参数是deferred对象,
- 上一链的旧deferred会调用[ done | fail | progress ]方法注册回调,该回调内容是:执行then方法对应的参数回调(fnDone, fnFail, fnProgress)。
- 1)如果参数回调执行后返回的结果是一个promise对象,我们就给该promise对象相应的回调列表添加回调,该回调是触发then方法返回的新promise对象的成功,失败,处理中(done,fail,progress)的回调列表中的所有回调。
- 当我们再给then方法进行链式地添加回调操作(done,fail,progress,always,then)时,就是给新deferred对象注册回调到相应的回调列表。
- 如果我们then参数fnDoneDefer, fnFailDefer, fnProgressDefer得到了解决,就会执行后面链式添加回调操作中的参数函数。
- 2)如果参数回调执行后返回的结果returned不是promise对象,就立刻触发新deferred对象相应回调列表的所有回调,且回调函数的参数是先前的执行返回结果returned。
- 当我们再给then方法进行链式地添加回调操作(done,fail,progress,always,then)时,就会立刻触发我们添加的相应的回调。
- 可以多个then连续使用,此功能相当于顺序调用异步回调。
$.ajax({
url: 't2.html',
dataType: 'html',
data: {
d: 4
}
}).then(function(){
console.log('success');
},function(){
console.log('failed');
}).then(function(){
console.log('second');
return $.ajax({
url: 'jquery-1.9.1.js',
dataType: 'script'
});
}, function(){
console.log('second f');
return $.ajax({
url: 'jquery-1.9.1.js',
dataType: 'script'
});
}).then(function(){
console.log('success2');
},function(){
console.log('failed2');
});
上面的代码,如果第一个对t2.html的请求成功输出success,就会执行second的ajax请求,接着针对该请求是成功还是失败,执行success2或者failed2。
如果第一个失败输出failed,然后执行second f的ajax请求(注意和上面的不一样),接着针对该请求是成功还是失败,执行success2或者failed2。
理解这些对失败处理很重要。
将我们上面序列化异步操作的代码使用then方法改造后,代码立马变得扁平化了,可读性也增强了:
var req1 = $.get('api1/data');
var req2 = $.get('api2/data');
var req3 = $.get('api3/data');
req1.then(function(req1Data){
return req2.done(otherFunc);
}).then(function(req2Data){
return req3.done(otherFunc2);
}).then(function(req3Data){
doneSomethingWithReq3();
});
4.接着介绍$.when的方法使用,主要是对多个deferred对象进行并行化操作,当所有deferred对象都得到解决就执行后面添加的相应回调。
$.when(
$.ajax({
url: 't2.html'
}),
$.ajax({
url: 'jquery-1.9.1-study.js'
})
).then(function(FirstAjaxSuccessCallbackArgs, SecondAjaxSuccessCallbackArgs){
console.log('success');
}, function(){
console.log('failed');
});
如果有一个失败了都会执行失败的回调。
将我们上面并行化操作的代码改良后:
$.when(
$.get('api1/data'),
$.get('api2/data'),
$.get('api3/data'),
{ key: 'value' }
).done();