文章出处:http://www.javashuo.com/article/p-vpaancmr-z.htmljavascript
jQuery1.5以前,若是须要屡次Ajax操做,咱们通常会使用下面的两种方式:html
1).串行调用Ajaxjava
$.ajax({ success: function() { $.ajax({ success: function() { $.ajax({ //callbacks... }); }); });
这种方式代码可读性差,效率低,晦涩难懂,调试和排错的复杂度大。jquery
2).并行调用Ajaxajax
var promises = []; $.ajax({ success: function() { promises.push('resolved'); check(); } }); $.ajax({ success: function() { promises.push('resolved'); check(); } }); $.ajax({ success: function() { promises.push('resolved'); check(); } }); var check = function() { //checks for all 3 values in the promises array }
这种方式对于callbacks函数调用来讲已经很不错了,并行取得数据,可读性良好。缺点就是代码冗长,可扩展性差,调试和排错的复杂度高。api
jQuery1.5以后,增长了deferred对象。所以能够用下面这种方式实现和上面一样的需求。数组
1)Promisepromise
var address = $.ajax({}); var tweets = $.ajax({}); var facebook = $.ajax({}); render_side_bar = function(address, tweets, facebook){ //render sidebar } render_no_side_bar = function () { } $.when(address, tweets, facebook).then(render_side_bar, render_no_side_bar)
能够看出,代码可读性良好,可扩展性高,而且大大下降了调试和排错的复杂度。app
那么问题来了,promises和deferred对象到底是个什么玩意呢?dom
deferred对象即延迟对象,它是jQuery 1.5版本引入的一种回调函数的解决方案,表明了将要完成的某种操做,而且提供了一些方法,帮助用户使用。
deferred对象是对Promises接口的实现。jQuery 1.5版本以及以后全部的Ajax返回的jqXHR对象就是一个deferred对象。
deferred对象的好处之一,就是它容许你为一个操做添加多个回调函数,这在传统的ajax中是没法实现的。
$.ajax("test.html") .done(function(){ alert("first success callback!");} ) .fail(function(){ alert("there is an error!"); } ) .done(function(){ alert("second success callback!");} );
deferred对象的好处之二,就是它容许你为多个操做指定同一个回调函数,这在传统的ajax中也是没法实现的。
$.when($.ajax({}), $.ajax({})) .done(function(){ alert("success!"); }) .fail(function(){ alert("error!"); });
deferred对象的好处之三,就是它再也不拘泥于ajax操做,任意的操做(ajax操做or本地操做/异步操做or同步操做)均可以使用deferred对象,指定回调函数。
一个很典型的耗时操做
var dfd = $.Deferred(); // create a deferred object var wait = function(dtd){ var tasks = function(){ alert("over!"); dtd.resolve(); // change the state of the deferred object from pending to resolved }; setTimeout(tasks,50000); return dtd; };
$.when(wait(dtd)) .done(function(){ alert("success!"); }) .fail(function(){ alert("error!"); });
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方法。相对于传统的写法,代码可读性提升了。
(1).生成deferred对象
var dfd = $.Deferred(); //create a deferred object
(2).deferred对象的状态
deferred对象有三种状态
state()方法返回deferred对象的当前状态。
$.Deferred().state(); // 'pending' $.Deferred().resolve().state(); // 'resolved' $.Deferred().reject().state(); // 'rejected'
(3).改变deferred对象的状态
调用deferred.resolve()
或者 deferred.resolveWith()
转换Deferred(递延)到resolved(解决)的状态,并当即执行设置中任何的doneCallbacks
。
var callbackFunc = function(){console.log(arguments[0]);} var dfd = $.Deferred(); dfd.done(callbackFunc); dfd.resolve("hello"); //'hello'
调用deferred.reject()
或者 deferred.rejectWith()
转换Deferred(递延)到rejected(拒绝)的状态,并当即执行设置中任何的failCallbacks
。
var callbackFunc = function(){console.log(arguments[0]);} var dfd = $.Deferred(); dfd.fail(callbackFunc); dfd.reject("fail"); //'fail'
(4).绑定回调函数
deferred对象状态改变的时候,会触发回调函数。任何回调使用deferred.then()
, deferred.always()
, deferred.done()或者
deferred.fail()
添加到这个对象都是排队等待执行。
var f1 = function(){console.log("done");}, f2 = function(){console.log("fail");}, f3 = function(){console.log("always");}; var dfd = $.Deferred(); dfd.done(f1).fail(f2).always(f3); //if dfd.resolve(); //'done' 'always' //if dfd.reject(); //'fail' 'always'
若是在状态更改后附加一个callback则会当即执行callback,所以没必要担忧deferred对象什么时候被resolved或者rejected,由于不管什么时候,参数都会正确地传递给callbacks。
var fun1 = function(){console.log(arguments[0]);}, fun1 = function(){console.log(arguments[0]);}; var dfd = $.Deferred(); dfd.done(fun1); dfd.resolve("hello"); //'hello' dfd.done(fun2); //'hello'
(1)$.Deferred([beforeStart]) -- 建立一个deferred对象,参数类型为Function,是一个在构造函数以前调用的函数。
var func = function(){console.log("start");} var dfd = $.Deferred(func); //'start' create a deferred object
(2)deferred.done(doneCallbacks [,doneCallbacks]) -- 当deferred(延迟)对象解决时,调用添加处理程序。
args:接受一个或者多个参数,全部的参数均可以是一个单一的函数或者函数数组,当deferred(延迟)对象解决时,doneCallbacks被调用。回调是依照他们添加的顺序执行的。
var func1 = function(){console.log("1");}, func2 = function(){console.log("2");}, func3 = function(){console.log("3");}; var dfd = $.Deferred(); dfd.done([func1,func2],func3,[func2,func1]); dfd.resolve(); // "1 2 3 2 1"
(3)deferred.fail(failCallbacks [,failCallbacks]) -- 当deferred(延迟)对象拒绝时,调用添加处理程序。
args:接受一个或者多个参数,全部的参数均可以是一个单一的函数或者函数数组,当deferred(延迟)对象拒绝时,failCallbacks被调用。回调是依照他们添加的顺序执行的。
var func1 = function(){console.log("1");}, func2 = function(){console.log("2");}, func3 = function(){console.log("3");}; var dfd = $.Deferred(); dfd.fail([func1,func2],func3,[func2,func1]); dfd.reject(); // "1 2 3 2 1"
(4)deferred.resolve(args) and deferred.resolveWith(context [,args]) -- 解决Deferred(延迟)对象,并根据给定的args参数(resolveWith给定context)调用任何doneCallbacks。
参数:args -- type(object),传递给回调函数(doneCallbacks)的可选的参数,
context -- type(object),Context(上下文)做为this对象传递给完成回调函数(doneCallbacks)。
var func = function(arg){console.log(arg);}; $.Deferred().done(func).resolve("done!"); //'done!'
var func = function(arg1,arg2){console.log(arg1.name + ',' + arg2);}; $.Deferred().done(func).resolve({name:'Lucy'},'How are you!'); // 'Lucy,How are you!'
resolve和resolveWith的区别就等同于fire和fireWith的区别。
var func = function () { console.log(this.name + ',' + arguments[0] + ' ' + arguments[1] + ' ' + arguments[2]); }; $.Deferred().done(func).resolveWith({ name: "Lucy" }, ["How", "are", "you!"]);//'Lucy,How are you!'
(5)deferred.reject(args) and deferred.rejectWith(context [,args]) -- 拒绝Deferred(延迟)对象,并根据给定的args参数(rejectWith给定context)调用任何failCallbacks。
参数:args -- type(object),传递给回调函数(doneCallbacks)的可选的参数,
context -- type(object),Context(上下文)做为this对象传递给完成回调函数(doneCallbacks)。
var func = function(arg){console.log(arg);}; $.Deferred().fail(func).reject("error!"); //'error!'
var func = function(ctx,arg){console.log(ctx.name + ',' + arg);}; $.Deferred().fail(func).reject({name:'Mark'},'What happend!'); // 'Mark,What happend!'
reject和rejectWith的区别就等同于fire和fireWith的区别。
var func = function () { console.log(this.name + ',' + arguments[0] + ' ' + arguments[1]); }; $.Deferred().fail(func).rejectWith({ name: "Mark" }, ["what", "happend!"]); // 'Mark,What happend!'
(6)deferred.promise([target]) -- 返回Deferred(延迟)的Promise(承诺)对象。
参数可选,无参数时返回一个Promise(承诺)对象,Promise(承诺)对象仅会暴露那些须要绑定额外的处理或判断状态的延迟方法(then
, done
, fail
, always
,pipe
, progress
, state
,和 promise
)时,并不会暴露任何用于改变状态的延迟方法(resolve
, reject
, notify
,resolveWith
, rejectWith
, 和 notifyWith
)。使用Promise(承诺)会阻止其余人破坏你制造的promise。
function asyncEvent() { var dfd = jQuery.Deferred(); // Resolve after a random interval setTimeout(function () { dfd.resolve("hurray"); }, Math.floor(400 + Math.random() * 2000)); // Reject after a random interval setTimeout(function () { dfd.reject("sorry"); }, Math.floor(400 + Math.random() * 2000)); // Show a "working..." message every half-second setTimeout(function working() { if (dfd.state() === "pending") { dfd.notify("working... "); setTimeout(working, 500); } }, 1); // Return the Promise so caller can't change the Deferred return dfd.promise(); } // Attach a done, fail, and progress handler for the asyncEvent $.when(asyncEvent()).then( function (status) { alert(status + ", things are going well"); }, function (status) { alert(status + ", you fail this time"); }, function (status) { alert(status); } );
有参数时,会将事件绑定到参数上,而后返回该参数对象(返回的实际是一个扩展的Promise(承诺)对象)。
var obj = { hello: function (name) { alert("Hello " + name); } }, // Create a Deferred dfd = $.Deferred(); // Set object as a promise dfd.promise(obj); // Resolve the deferred dfd.resolve("John"); // Use the object as a Promise obj.done(function (name) { obj.hello(name); // will alert "Hello John" }).hello("Karl");
(7)$.when(deferreds) -- 提供一种方法来执行一个或多个对象的回调函数。
参数:type(Deferred),一个或多个延迟对象,或者普通的JavaScript对象。
function func() { var dfd = $.Deferred(); setTimeout(function () { dfd.resolve("hurry"); }, 500); return dfd.promise(); }; $.when(func()).done(function (arg) { alert(arg); /*alert "hurry"*/ });
2.参数传入一个非Deferred和Promise对象,那么该参数会被当成一个被解决(resolved)的延迟对象,而且绑定到上面的任何doneCallbacks都会被当即执行。
$.when( { name: 123 } ).done( function(arg) { alert(arg.name); } /* alerts "123" */ );
3.无参数,返回一个resolved(解决)状态的Promise对象。
$.when().state(); // "resolved"
4.参数为多个Deferred对象,该方法根据一个新的“宿主” Deferred(延迟)对象,跟踪全部已经过Deferreds汇集状态,返回一个Promise对象。当全部的延迟对象被解决(resolve)时,“宿主” Deferred(延迟)对象才会解决(resolved)该方法,或者当其中有一个延迟对象被拒绝(rejected)时,“宿主” Deferred(延迟)对象就会reject(拒绝)该方法。
var d1 = $.Deferred(); var d2 = $.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(延迟)对象解决,拒绝或仍在进行中时,调用添加处理程序。
参数:
其实,then方法能够理解成,把done(),fail(),progress()合在一块儿写。
var filterResolve = function () { var dfd = $.Deferred(), filtered = dfd.then(function (value) { return value * 2; }); dfd.resolve(5); filtered.done(function (value) { console.log(value); }); }; filterResolve(); //'10' var defer = $.Deferred(), filtered = defer.then(null, function (value) { return value * 3; }); defer.reject(6); filtered.fail(function (value) { alert("Value is 3*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) and deferred.notifyWith(context,args)
参数我就很少解释了,和上面resolve()和reject()的参数都是同样,只不过这些参数是progressCallbacks,即进行中的回调。
function doSomething() { var dfd = $.Deferred(); var count = 0; var intervalId = setInterval(function() { dfd.notify(count++); count > 3 && clearInterval(intervalId); }, 500); return dfd.promise(); }; var promise = doSomething(); promise.progress(function (prog) { console.log(prog); // '0', '1', '2', '3' });
notifyWith()和notify()的区别就是fire()和fireWith()的区别,用法请参开resolve()和resolveWith()的例子。
(12)deferred.progress(progressCallbacks, progressCallbacks) -- 当Deferred(延迟)对象生成正在执行中的进度通知时,调用progressCallbacks。
前一个参数是函数或数组,后一个参数也是函数或数组,可是是可选的。
这个方法的解释很是的抽象,刚开始的时候不知道什么叫生成正在执行中的进度通知,其实这个progress()是须要和notify()或者notifyWith()方法结合一块儿使用的,代码请参考notify()的示例。
(13).promise([type] [, target]) -- 返回一个Promise(承诺)对象,用来观察当某种类型的全部行动绑定到集合,排队与否仍是已经完成。此方法主要用于animations和ajax操做。
参数:type(默认:fx)(类型:String)须要待观察队列类型,这个概念很模糊,拿默认值来讲,"fx"意味着返回被resolve的Promise对象的时机,是在全部被选中元素的动画都完成时发生的。
target(类型:PlainObject)将要绑定promise方法的对象,也就是若是提供target参数,.promise()在该target参数上添加方法,而后返回这个对象,而不是建立一个新的,这种方式适用于在一个已经存在的对象上添加 Promise行为的状况。不提供该参数的话,.promise()会返回一个新建立的Promise对象。
1.在没有激活动画的集合上调用.promise(),返回一个resolved的Promise对象,此时该Promise对象上的doneCallbacks会当即执行。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script src="jquery-1.9.0.js"></script> <script type="text/javascript"> $(function () { var div = $("<div />"); var promise = div.promise(); promise.done(function (arg1) { // will fire right away and alert "true" alert(this === div && arg1 === div); }); }); </script> </head> <body> <div></div> </body> </html>
2.当全部的动画结果时(包括哪些在动画回调函数和以后添加的回调函数中初始化的动画),resolve返回的Promise。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script src="jquery-1.9.0.js"></script> <style> div { height: 50px; width: 50px; float: left; margin-right: 10px; display: none; background-color: #090; } </style> <script type="text/javascript"> $(function () { $("button").bind("click", function () { $("p").append("Started..."); $("div").each(function (i) { $(this).fadeIn().fadeOut(1000 * (i + 1)); }); $("div").promise().done(function () { $("p").append(" Finished! "); }); }); }); </script> </head> <body> <button>Go</button> <p>Ready...</p> <div></div> <div></div> <div></div> <div></div> </body> </html>
(14)deferred.isRejected() 和 deferred.isResolved() -- 从jQuery 1.7开始被弃用,较新版本的jQuery类库中已经被删除,可使用state()方法代替这两个方法。
(15)deferred.pipe() -- 从jQuery 1.8开始被弃用,可以使用deferred.then()替代。
上面讲了不少,那么咱们究竟在什么状况下使用Deferred对象和Promises对象呢?
(1)复杂的动画
不知道动画何时结束,可是又必须在动画结束的时候作一些操做或者是启动其余的动画,这种状况下,若是采用其余的方式,很容易致使代码可读性差,尤为是还夹带着一些其它的操做,好比渲染、表单操做等,如今jQuery会为你的动画操做返回一个Promise,这样这些动画能够进行链式操做。
(2)处理队列
window.queue = $.when() $('#list').on('click', function() { window.queue = window.queue.then(function() { //do the thing }) } )
(3)The Wait promise
function wait(ms) { var deferred = $.Deferred(); setTimeout(function(){deferred.resolve()}, ms); return deferred.promise(); } wait(1500).then(function () { // After 1500ms this will be executed });
(4)典型的Ajax操做
$.when($.ajax({}), $.ajax({})) .done(function(){ alert("success!"); }) .fail(function(){ alert("error!"); });
(5)一些耗时的大循环操做
[1] Graham Jenson, JQuery Promises and Deferreds: I promise this will be short
[2] 阮一峰, jQuery的deferred对象详解
[3]jQuery API, Deferred Object
[4]José F. Romaniello, Understanding JQuery.Deferred and Promise
[5]Matt Baker, jQuery.Deferred is the most important client-side tool you have
[6]Stackoverflow JQuery Deferred. Using $.when and .progress()
[7]http://javascript.ruanyifeng.com/jquery/deferred.html
$.Deferred().state();//"pending"