看到“jquery里面的deferred对象”这样的标题,你兴许会疑惑不解,jquery里面有deferred对象?在哪里,我什么时候用到过这个deferred对象?别说你没有用过,看下面的代码:
<script>
var ret=$.ajax({
type:'post',
data:{},
url:"MyServlet",
success:function(data){
console.log("调用成功");
},
error:function(data){
console.log("调用失败");
}
});
console.log(ret);
</script>
陌生吗?我们平常用jquery的ajax的时候,最常用的写法就是:
$.ajax({
type:'post',
data:{},
url:"MyServlet",
success:function(data){},
error:function(data){}
});
我在上边只是加了个返回值,并把返回值打印了出来,返回值是什么呢?如下:
可以看到,jquery的ajax函数返回值是一个对象,而这个对象就是我今天要说的主角了:deferred对象。
除了我们上边的ajax写法比较常用之外,我们写也可以这样来写ajax代码:
<script>
$.ajax({
type:'post',
data:{},
url:"MyServlet",
}).done(function(data){
console.log("成功");
}).fail(function(data){
console.log("失败");
});
</script>
执行结果:
为什么可以这样写,知道了吗?因为ajax返回的是deferred对象,而done和fail都是deferred对象的方法?到这里有必要解释一下什么是deferred对象了。
defer翻译过来是"延迟"的意思,deferred对象的含义也可以按着这个方向来理解-------"延迟"到未来某个点再执行。这样说比较抽象,还是明白点说。我们可能遇到过这样的情况,b逻辑需要等待a完成之后再执行,而a执行完成的时间不确定,怎么确保b的执行正好在a完成之后呢?此时,我们可以把b称作a的回调函数,针对这个问题的解决,每种语言都有自己的实现方法,而jquery的解决方案就是deferred对象。
deferred对象解决了如何处理耗时操作的回调函数的问题。
明白了什么是deferred对象以及deferred对象为什么会出现,再来看看deferred对象的方法都是代表什么意思呢。我先列出来常用的,底下再分别演示:
(1) $.Deferred():手动生成一个deferred对象。
(2) deferred.done() :操作成功之后的回调函数,在ajax里面就相当于success
(3) deferred.fail() :操作失败时的回调函数,在ajax里面就相当于error
(4) deferred.promise() 返回一个新的deferred对象,该对象的运行状态无法被改变。deferred对象有三种状态:未完成、已完成、已失败
(5) deferred.resolve() 手动改变deferred对象的运行状态为"已完成",从而立即触发done()方法。
(6)deferred.reject() 手动改变deferred对象的运行状态变为"已失败",从而立即触发fail()方法。
(7) $.when(deferred1[,deferred2]) 为多个操作指定回调函数。
(8)deferred.then():有时为了省事,可以把done()和fail()合在一起写,这就是then()方法。
$.ajax({
type:'post',
data:{},
url:"MyServlet",
}).then(successFunc, failureFunc );
如果then()有两个参数,那么第一个参数是done()方法的回调函数,第二个参数是fail()方法的回调方法。如果then()只有一个参数,那么等同于done()。
(9)deferred.always():这个方法也是用来指定回调函数的,它的作用是,不管调用的是deferred.resolve()还是deferred.reject(),最后总是要执行always()指定的回调函数。
先来看看always:
<script>
$.ajax({
type:'post',
data:{},
url:"MyServlet",
}).done(function(data){
console.log("成功");
}).fail(function(data){
console.log("失败");
}).always(function(){
console.log("我总是被执行");
});
</script>
执行结果:
再来看看$.when的使用:
$.when($.ajax("MyServlet"),$.ajax("MyServlet2")).done(function(){
console.log("都执行完成了");
});
$.when允许你传入一个或多个deferred对象,即可以执行一个或多个deferred对象的回调函数。
除了可以传入ajax返回的deferred对象,你还可以传入一个普通函数f,此时表示的是f执行完之后的回调函数。
有下面的代码:
<script>
function fun1(){
setTimeout("getData()",3000);
}
function getData(){
console.log("getData");
}
fun1();
</script>
我们知道,上边的代码含义是在3秒之后调用getData()函数,这是没有问题的,现在如果我想是想在getData()函数调用完成并成功返回结果之后你给我打印出“调用getData完成done”怎么实现呢?显然,这里牵涉到打印顺序的问题,如果你像下面这样写:
<script>
function fun1(){
setTimeout("getData()",3000);
}
function getData(){
console.log("getData");
}
$.when(fun1()).done(function(){
console.log("done");
}).fail(function(){
console.log("fail");
});
</script>
得到的结果是下面这个样子:
显然,这个打印顺序并不符合要求“getData()函数调用完成并成功返回结果之后”。
需要改写为下面这个样子。
<script>
function fun1(){
var dtd = $.Deferred(); //在函数内部,新建一个Deferred对象
setTimeout(getData,3000);
function getData(){
console.log("getData");
dtd.resolve(); // 改变Deferred对象的执行状态为“已完成”,当Deferred对象对象
//的状态改变为"已完成"的时候,就会立马触发done方法
}
return dtd.promise();//如果直接返回dtd,那么在fun1外部也可以改变resolve对象了,不安全。
//dtd.promise();和dtd都是deferred对象,所不同的仅仅是前者屏蔽了resolve和reject方法
}
$.when(fun1()).done(function(){
console.log("done");
}).fail(function(){
console.log("fail");
});
</script>
执行结果: