jQuery.deferred对象使用详解
一、前言
jQuery1.5之前,如果需要多次Ajax操作,我们一般会使用下面的两种方式:
1).串行调用Ajax
$.ajax({success:function(){ $.ajax({success:function(){ $.ajax({//callbacks... }); }); });
这种方式代码可读性差,效率低,晦涩难懂,调试和排错的复杂度大。
2).并行调用Ajax
varpromises=[]; $.ajax({ success:function(){ promises.push('resolved'); check(); } }); $.ajax({ success:function(){ promises.push('resolved'); check(); } }); $.ajax({ success:function(){ promises.push('resolved'); check(); } }); varcheck=function(){//checksforall3valuesinthepromisesarray}
这种方式对于callbacks函数调用来说已经很不错了,并行取得数据,可读性良好。缺点就是代码冗长,可扩展性差,调试和排错的复杂度高。
jQuery1.5之后,增加了deferred对象。因此可以用下面这种方式实现和上面同样的需求。
1)Promise
varaddress=$.ajax({}); vartweets=$.ajax({}); varfacebook=$.ajax({}); render_side_bar=function(address,tweets,facebook){ //rendersidebar } render_no_side_bar=function(){} $.when(address,tweets,facebook).then(render_side_bar,render_no_side_bar)
可以看出,代码可读性良好,可扩展性高,并且大大降低了调试和排错的复杂度。
那么问题来了,promises和deferred对象究竟是个什么玩意呢?
二、详解
2.什么是deferred对象?
deferred对象即延迟对象,它是jQuery1.5版本引入的一种回调函数的解决方案,代表了将要完成的某种操作,并且提供了一些方法,帮助用户使用。
deferred对象是对Promises接口的实现。jQuery1.5版本以及之后所有的Ajax返回的jqXHR对象就是一个deferred对象。
2.deferred对象的几大好处
2.1.为同一操作指定多个回调函数
deferred对象的好处之一,就是它允许你为一个操作添加多个回调函数,这在传统的ajax中是无法实现的。
$.ajax("test.html") .done(function(){alert("firstsuccesscallback!");}) .fail(function(){alert("thereisanerror!");}) .done(function(){alert("secondsuccesscallback!");});
2.2.为多个操作指定同一个回调函数
deferred对象的好处之二,就是它允许你为多个操作指定同一个回调函数,这在传统的ajax中也是无法实现的。
$.when($.ajax({}),$.ajax({})) .done(function(){alert("success!");}) .fail(function(){alert("error!");});
2.3.非Ajax操作的回调函数
deferred对象的好处之三,就是它不再拘泥于ajax操作,任意的操作(ajax操作or本地操作/异步操作or同步操作)都可以使用deferred对象,指定回调函数。
一个很典型的耗时操作
vardfd=$.Deferred();//createadeferredobject varwait=function(dtd){ vartasks=function(){ alert("over!"); dtd.resolve();//changethestateofthedeferredobjectfrompendingtoresolved }; setTimeout(tasks,50000); returndtd; };
$.when(wait(dtd)) .done(function(){alert("success!");}) .fail(function(){alert("error!");});
2.4.链式调用
jQuery中传统的ajax操作是这样的:
$.ajax({ url:"", success:function(){ alert("success!"); }, error:function(){ alert("error!"); } });
其中success指定ajax操作成功后的回调函数,error指定ajax操作失败后的回调函数。jQuery1.5版本之前,Ajax操作返回的是一个XMLHTTPRequest对象,不支持链式操作。1.5版本开始,ajax操作返回的是jqXHR对象,这是一个deferred对象,而deferred对象一个显著的好处就是可以进行链式操作,因为deferred对象的所有方法返回的均是deferred对象。
现在的ajax操作的写法是:
$.ajax({}) .done(function(){alert("success!");}) .fail(function(){alert("fail!");});
两种写法对比可以很明显的看出来,done()相当于传统ajax操作的success方法,fail()相当于传统ajax操作的fail方法。相对于传统的写法,代码可读性提高了。
3.deferred对象的方法
3.1基本用法
(1).生成deferred对象
vardfd=$.Deferred();//createadeferredobject
(2).deferred对象的状态
deferred对象有三种状态
pending:表示操作处于未完成的状态,任何deferred(延迟)对象开始于pending状态。
resolved:表示操作成功。
rejected:表示操作失败。
state()方法返回deferred对象的当前状态。
$.Deferred().state();//'pending' $.Deferred().resolve().state();//'resolved' $.Deferred().reject().state();//'rejected'
(3).改变deferred对象的状态
调用deferred.resolve()或者deferred.resolveWith()转换Deferred(递延)到resolved(解决)的状态,并立即执行设置中任何的doneCallbacks。
varcallbackFunc=function(){console.log(arguments[0]);} vardfd=$.Deferred(); dfd.done(callbackFunc); dfd.resolve("hello");//'hello'
调用deferred.reject()或者deferred.rejectWith()转换Deferred(递延)到rejected(拒绝)的状态,并立即执行设置中任何的failCallbacks。
varcallbackFunc=function(){console.log(arguments[0]);} vardfd=$.Deferred(); dfd.fail(callbackFunc); dfd.reject("fail");//'fail'
(4).绑定回调函数
deferred对象状态改变的时候,会触发回调函数。任何回调使用deferred.then(),deferred.always(),deferred.done()或者deferred.fail()添加到这个对象都是排队等待执行。
pending-->resolved,执行设置中任何的doneCallbacks(done()指定),参数由resolved传递给doneCallbacks。
pending-->rejected,执行设置中任何的failCallbacks(fail()指定),参数由resolved传递给failCallbacks。
pending-->resolved/rejected,执行always()指定的callbacks,参数由resolved传递给callbacks。
varf1=function(){console.log("done");}, f2=function(){console.log("fail");}, f3=function(){console.log("always");}; vardfd=$.Deferred(); dfd.done(f1).fail(f2).always(f3); //if dfd.resolve();//'done''always' //if dfd.reject();//'fail''always'
如果在状态更改后附件一个callback则会立即执行callback,因此不必担心deferred对象何时被resolved或者rejected,因为无论何时,参数都会正确地传递给callbacks。
varfun1=function(){console.log(arguments[0]);}, fun1=function(){console.log(arguments[0]);}; vardfd=$.Deferred(); dfd.done(fun1); dfd.resolve("hello");//'hello' dfd.done(fun2);//'hello'
3.2.deferred对象的方法
(1)$.Deferred([beforeStart])--创建一个deferred对象,参数类型为Function,是一个在构造函数之前调用的函数。
varfunc=function(){console.log("start");} vardfd=$.Deferred(func);//'start'createadeferredobject
(2)deferred.done(doneCallbacks[,doneCallbacks])--当deferred(延迟)对象解决时,调用添加处理程序。
args:接受一个或者多个参数,所有的参数都可以是一个单一的函数或者函数数组,当deferred(延迟)对象解决时,doneCallbacks被调用。回调是依照他们添加的顺序执行的。
varfunc1=function(){console.log("1");}, func2=function(){console.log("2");}, func3=function(){console.log("3");}; vardfd=$.Deferred(); dfd.done([func1,func2],func3,[func2,func1]); dfd.resolve();//"12321"
(3)deferred.fail(failCallbacks[,failCallbacks])--当deferred(延迟)对象拒绝时,调用添加处理程序。
args:接受一个或者多个参数,所有的参数都可以是一个单一的函数或者函数数组,当deferred(延迟)对象拒绝时,failCallbacks被调用。回调是依照他们添加的顺序执行的。
varfunc1=function(){console.log("1");}, func2=function(){console.log("2");}, func3=function(){console.log("3");}; vardfd=$.Deferred(); dfd.fail([func1,func2],func3,[func2,func1]); dfd.reject();//"12321"
(4)deferred.resolve(args)anddeferred.resolveWith(context[,args])--解决Deferred(延迟)对象,并根据给定的args参数(resolveWith给定context)调用任何doneCallbacks。
参数:args--type(object),传递给回调函数(doneCallbacks)的可选的参数,
context--type(object),Context(上下文)作为this对象传递给完成回调函数(doneCallbacks)。
varfunc=function(arg){console.log(arg);}; $.Deferred().done(func).resolve("done!");//'done!' varfunc=function(arg1,arg2){console.log(arg1.name+','+arg2);}; $.Deferred().done(func).resolve({name:'Lucy'},'Howareyou!');//'Lucy,Howareyou!'
resolve和resolveWith的区别就等同于fire和fireWith的区别。
varfunc=function(){ console.log(this.name+','+arguments[0]+''+arguments[1]+''+arguments[2]); }; $.Deferred().done(func).resolveWith({name:"Lucy"},["How","are","you!"]);//'Lucy,Howareyou!'
(5)deferred.reject(args)anddeferred.rejectWith(context[,args])--拒绝Deferred(延迟)对象,并根据给定的args参数(rejectWith给定context)调用任何failCallbacks。
参数:args--type(object),传递给回调函数(doneCallbacks)的可选的参数,
context--type(object),Context(上下文)作为this对象传递给完成回调函数(doneCallbacks)。
varfunc=function(arg){console.log(arg);}; $.Deferred().fail(func).reject("error!");//'error!' varfunc=function(ctx,arg){console.log(ctx.name+','+arg);}; $.Deferred().fail(func).reject({name:'Mark'},'Whathappend!');//'Mark,Whathappend!'
reject和rejectWith的区别就等同于fire和fireWith的区别。
varfunc=function(){ console.log(this.name+','+arguments[0]+''+arguments[1]); }; $.Deferred().fail(func).rejectWith({name:"Mark"},["what","happend!"]);//'Mark,Whathappend!'
(6)deferred.promise([target])--返回Deferred(延迟)的Promise(承诺)对象。
参数可选,无参数时返回一个Promise(承诺)对象,Promise(承诺)对象仅会暴露那些需要绑定额外的处理或判断状态的延迟方法(then,done,fail,always,pipe,progress,state,和promise)时,并不会暴露任何用于改变状态的延迟方法(resolve,reject,notify,resolveWith,rejectWith,和notifyWith)。使用Promise(承诺)会阻止其他人破坏你制造的promise。
functionasyncEvent(){ vardfd=jQuery.Deferred(); //Resolveafterarandominterval setTimeout(function(){ dfd.resolve("hurray"); },Math.floor(400+Math.random()*2000)); //Rejectafterarandominterval setTimeout(function(){ dfd.reject("sorry"); },Math.floor(400+Math.random()*2000)); //Showa"working..."messageeveryhalf-second setTimeout(functionworking(){ if(dfd.state()==="pending"){ dfd.notify("working..."); setTimeout(working,500); } },1); //ReturnthePromisesocallercan'tchangetheDeferred returndfd.promise(); } //Attachadone,fail,andprogresshandlerfortheasyncEvent $.when(asyncEvent()).then( function(status){ alert(status+",thingsaregoingwell"); }, function(status){ alert(status+",youfailthistime"); }, function(status){ alert(status); } );
有参数时,会将事件绑定到参数上,然后返回该参数对象(返回的实际是一个扩展的Promise(承诺)对象)。
varobj={ hello:function(name){ alert("Hello"+name); } }, //CreateaDeferred dfd=$.Deferred(); //Setobjectasapromise dfd.promise(obj); //Resolvethedeferred dfd.resolve("John"); //UsetheobjectasaPromise obj.done(function(name){ obj.hello(name);//willalert"HelloJohn" }).hello("Karl");
(7)$.when(deferreds)--提供一种方法来执行一个或多个对象的回调函数。
参数:type(Deferred),一个或多个延迟对象,或者普通的JavaScript对象。
参数仅传入一个单独的Deferred对象,返回它的Promise对象。
functionfunc(){ vardfd=$.Deferred(); setTimeout(function(){ dfd.resolve("hurry"); },500); returndfd.promise(); }; $.when(func()).done(function(arg){ alert(arg);/*alert"hurry"*/ });
参数传入一个非Deferred和Promise对象,那么该参数会被当成一个被解决(resolved)的延迟对象,并且绑定到上面的任何doneCallbacks都会被立即执行。
$.when({name:123}).done( function(arg){alert(arg.name);}/*alerts"123"*/ );
无参数,返回一个resolved(解决)状态的Promise对象。
$.when().state();//"resolved"
参数为多个Deferred对象,该方法根据一个新的“宿主”Deferred(延迟)对象,跟踪所有已通过Deferreds聚集状态,返回一个Promise对象。当所有的延迟对象被解决(resolve)时,“宿主”Deferred(延迟)对象才会解决(resolved)该方法,或者当其中有一个延迟对象被拒绝(rejected)时,“宿主”Deferred(延迟)对象就会reject(拒绝)该方法。
vard1=$.Deferred(); vard2=$.Deferred(); $.when(d1,d2).done(function(v1,v2){ console.log(v1);//"Fish" console.log(v2);//"Pizza" }); d1.resolve("Fish"); d2.resolve("Pizza");
(8)deferred.then(doneFilter[,failFilter][,progressFilter])--当Deferred(延迟)对象解决,拒绝或仍在进行中时,调用添加处理程序。
参数:
doneFilter-- type(Function),当Deferred(延迟)对象得到解决时被调用的一个函数。
failFilter-- type(Function),当Deferred(延迟)对象拒绝时被调用的一个函数,可选。
progressFilter-- type(Function),当Deferred(延迟)对象生成进度通知时被调用的一个函数,可选。
其实,then方法可以理解成,把done(),fail(),progress()合在一起写。
varfilterResolve=function(){ vardfd=$.Deferred(), filtered=dfd.then(function(value){returnvalue*2;}); dfd.resolve(5); filtered.done(function(value){console.log(value);}); }; filterResolve();//'10' vardefer=$.Deferred(), filtered=defer.then(null,function(value){ returnvalue*3; }); defer.reject(6); filtered.fail(function(value){ alert("Valueis3*6="+value); });
(9)deferred.always(alwaysCallbacks[,alwaysCallbacks])--当Deferred(延迟)对象解决或拒绝时,执行alwaysCallbacks。
顾名思义,只要Deferred对象的状态发生更改(解决或者拒绝)均会调用alwaysCallbacks。
(10)deferred.state()--获取一个Deferred(延迟)对象的当前状态,不接受任何参数。
$.Deferred().state();//"pending"
上面讲述过deferre(延迟)对象的三种状态,这个方法对于debug非常有用,例如,在准备reject一个deferred对象之前,判断它是否处于resolved状态。
(11)deferred.notify(args)anddeferred.notifyWith()
(12)deferred.progress()
(13)deferred.pipe()
(14).promise()
(15)deferred.isRejected()和deferred.isResolved()-- 从jQuery1.7开始被弃用,较新版本的jQuery类库中已经被删除,可以使用state()方法代替这两个方法。
(16)deferred.pipe()--从jQuery1.8开始被弃用。
4.什么情况下使用deferred对象和Promises?
上面讲了很多,那么我们究竟在什么情况下使用Deferred对象和Promises对象呢?
(1)复杂的动画
不知道动画什么时候结束,但是又必须在动画结束的时候做一些操作或者是启动其他的动画,这种情况下,如果采用其他的方式,很容易导致代码可读性差,尤其是还夹带着一些其它的操作,比如渲染、表单操作等,现在jQuery会为你的动画操作返回一个Promise,这样这些动画可以进行链式操作。
(2)处理队列
window.queue=$.when()$('#list').on('click',function(){window.queue=window.queue.then(function(){//dothething})})
(3)TheWaitpromise
functionwait(ms){ vardeferred=$.Deferred(); setTimeout(function(){deferred.resolve()},ms); returndeferred.promise(); } wait(1500).then(function(){ //After1500msthiswillbeexecuted });
(4)典型的Ajax操作
$.when($.ajax({}),$.ajax({})) .done(function(){alert("success!");}) .fail(function(){alert("error!");});
(5)一些耗时的大循环操作
以上就是本文的全部内容,希望对大家的学习有所帮助。