一、前言

jQuery1.5之前,如果需要多次Ajax操作,我们一般会使用下面的两种方式:

1).串行调用Ajax

$.ajax({ success: function() {
$.ajax({ success: function() {
$.ajax({ //callbacks...
});
});
});

这种方式代码可读性差,效率低,晦涩难懂,调试和排错的复杂度大。

2).并行调用Ajax

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函数调用来说已经很不错了,并行取得数据,可读性良好。缺点就是代码冗长,可扩展性差,调试和排错的复杂度高。

jQuery1.5之后,增加了deferred对象。因此可以用下面这种方式实现和上面同样的需求。

1)Promise

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)

可以看出,代码可读性良好,可扩展性高,并且大大降低了调试和排错的复杂度。

那么问题来了,promises和deferred对象究竟是个什么玩意呢?

二、详解

1.什么是deferred对象?

deferred对象即延迟对象,它是jQuery 1.5版本引入的一种回调函数的解决方案,代表了将要完成的某种操作,并且提供了一些方法,帮助用户使用。

deferred对象是对Promises接口的实现。jQuery 1.5版本以及之后所有的Ajax返回的jqXHR对象就是一个deferred对象。

2.deferred对象的几大好处

2.1.为同一操作指定多个回调函数

deferred对象的好处之一,就是它允许你为一个操作添加多个回调函数,这在传统的ajax中是无法实现的。

$.ajax("test.html")
  .done(function(){ alert("first success callback!");} )
  .fail(function(){ alert("there is an error!"); } )
  .done(function(){ alert("second success callback!");} );

2.2.为多个操作指定同一个回调函数

deferred对象的好处之二,就是它允许你为多个操作指定同一个回调函数,这在传统的ajax中也是无法实现的。

$.when($.ajax({}), $.ajax({}))
  .done(function(){ alert("success!"); })
  .fail(function(){ alert("error!"); });

2.3.非Ajax操作的回调函数

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!"); });

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对象

var dfd = $.Deferred(); //create a deferred object

(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

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()添加到这个对象都是排队等待执行。

  • pending-->resolved,执行设置中任何的doneCallbacks(done()指定),参数由resolved传递给doneCallbacks。
  • pending-->rejected,执行设置中任何的failCallbacks(fail()指定),参数由reject传递给failCallbacks。
  • pending-->resolved/rejected,执行always()指定的callbacks,参数由resolved/rejected传递给callbacks。
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'

3.2.deferred对象的方法

(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(承诺)对象仅会暴露那些需要绑定额外的处理或判断状态的延迟方法(thendonefailalways,pipeprogressstate,和 promise)时,并不会暴露任何用于改变状态的延迟方法(resolverejectnotify,resolveWithrejectWith, 和 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对象。

  1. 参数仅传入一个单独的Deferred对象,返回它的Promise对象。

    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(延迟)对象解决,拒绝或仍在进行中时,调用添加处理程序。

参数:

  • doneFilter --   type(Function),当Deferred(延迟)对象得到解决时被调用的一个函数。
  • failFilter --   type(Function),当Deferred(延迟)对象拒绝时被调用的一个函数,可选。
  • progressFilter --   type(Function),当Deferred(延迟)对象生成进度通知时被调用的一个函数,可选。

其实,then方法可以理解成,把done(),fail(),progress()合在一起写。

var filterResolve = function () {
var dfd = $.Deferred(),
filtered = dfd.then(function (value) { return value * ; });
dfd.resolve();
filtered.done(function (value) { console.log(value); });
};
filterResolve(); //'10' var defer = $.Deferred(),
filtered = defer.then(null, function (value) {
return value * ;
}); defer.reject();
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()替代。

4.什么情况下使用deferred对象和Promises?

上面讲了很多,那么我们究竟在什么情况下使用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)一些耗时的大循环操作

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

未完待续。。。

jQuery.Deferred对象的更多相关文章

  1. javascript --- jQuery --- Deferred对象

    javascript --- jQuery --- Deferred对象 javascript的函数式编程是多么引人入胜,jQuery使代码尽可能的精简,intelligent! defer - 必应 ...

  2. JQuery Deferred 对象剖析

    JQuery 中利用 Deferred 对象提供类似 ES2016(aka. es7) 中 Promise 的功能. JQuery 中的 AJAX 请求函数返回的就是 Deferred 对象. 通过使 ...

  3. JQuery Deferred 对象

    http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html <jQu ...

  4. 使用JQuery Deferred对象的then() 解决多个AJAX操作顺序依赖的问题

    原文地址:http://www.2cto.com/kf/201507/424202.html 之前的文章javascript异步代码的回调地狱中提到了编写AJAX代码经常遇到的3个问题,现在我们看下如 ...

  5. jQuery Deferred对象详细源码分析(-)

    本系列文章讲介绍这个Deferred东西到底拿来干什么,从1.5版本加进来,jQuery的很多代码都重写了.直接先上源码分析了,清楚了源码分析,下节将讲具体的应用 以及应用场景. 创建对象 var d ...

  6. JQuery Deferred对象使用小结

    场景描述 如下,打开页面时,获取默认选中的项目,同时也会初始化Combobox下拉框下拉列表数据 问题描述 获取默认选中项目及下拉列表的js函数位于common.js文件,类似如下: // 根据项目类 ...

  7. 利用 Jquery Deferred 异步你的程序

    最近在做公司QA系统改造时,有这样的一个场景. QA系统中有些数据项需要从JIRA平台(一个国外项目与事务跟踪工具)中获取,JIRA平台提供了很完善的Rest API. 现在的要求是,在QA系统中提交 ...

  8. jquery管理ajax异步-deferred对象

    今天跟大家分享一个jquery中的对象-deferred.其实早在jquery1.5.0版本中就已经引入这个对象了.不过可能在实际开发过程中用到的并不多,所以没有太在意. 这里先不说deferred的 ...

  9. jQuery的deferred对象详解

    jQuery的deferred对象详解请猛击下面的链接 http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_ ...

随机推荐

  1. Android.mk各种文件编译汇总

    一.源代码编译 1.1 so预编译 LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libAppArea LOCAL ...

  2. List<T>实体去重分组:

    实体去重分组: //实体类 public class Province { public string id { get; set; } public string name { get; set; ...

  3. PERFORMANCE_SCHEMA 详解

    http://keithlan.github.io/2015/07/17/22_performance_schema/ http://www.markleith.co.uk/ http://www.c ...

  4. BaseAdapter优化深入分析

    BaseAdapter是一个数据适配器,将我们提供的数据格式化为ListView可以显示的数据,BaseAdapter的优化直接影响到ListView的显示效率. 我们都知道,ListView自带有回 ...

  5. WebService学习笔记系列(二)

    soap(简单对象访问协议),它是在http基础之上传递xml格式数据的协议.soap协议分为两个版本,soap1.1和soap1.2. 在学习webservice时我们有一个必备工具叫做tcpmon ...

  6. C#百分比式布局

    图一:原始窗口 图二:放大窗口 实现:窗体内添加一个panel1(Dock:Left),在窗体空余部分添加第二个panel2(Dock:Fill),窗体分为两部分. 在panel2内添加一个panel ...

  7. css3新增加的选择器

    css3新增加的选择器 一.属性选择器: E[attr] 只要有属性名E[attr=value] 属性名=属性值E[attr~=blue] 包含这个blue整个单词就可以E[attr^=c] 以这个字 ...

  8. navicat 数据库管理工具快捷键

    最近在使用navicat 管理数据库中,因为经常要写一些sql的,但是每次都要鼠标点击运行,感觉很不爽,于是找到navicat(以下) 快捷键(最常用的): ctrl + q 打开查询窗口 ctrl ...

  9. IIS7报错:如果要使用托管的处理程序,请安装 ASP.NET

    转载至码农SeraphWU IIS7报错:如果要使用托管的处理程序,请安装 ASP.NET 进入CMD 输入如下命令即可 "%WINDIR%\Microsoft.NET\Framework\ ...

  10. Socket 错误一览表

    出现网络联机错误Socket error #11001 表示您的计算机无法连上服务器,请检查您的Proxy设定以及Proxy相关账号,或暂时取消您防毒软件的「个人防火墙」. · 出现网络联机错误Soc ...