jQuery.deferred对象使用详解


Posted in Javascript onMarch 18, 2016

一、前言
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对象究竟是个什么玩意呢?

二、详解
2.什么是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()指定),参数由resolved传递给failCallbacks。
pending-->resolved/rejected,执行always()指定的callbacks,参数由resolved传递给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(承诺)对象仅会暴露那些需要绑定额外的处理或判断状态的延迟方法(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对象。

参数仅传入一个单独的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"*/
});

参数传入一个非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(拒绝)该方法。

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 * 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()

(12)deferred.progress()

(13)deferred.pipe()

(14).promise()

(15)deferred.isRejected() 和 deferred.isResolved() --  从jQuery 1.7开始被弃用,较新版本的jQuery类库中已经被删除,可以使用state()方法代替这两个方法。

(16)deferred.pipe() -- 从jQuery 1.8开始被弃用。

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)一些耗时的大循环操作

以上就是本文的全部内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
新发现一个骗链接的方法(js读取cookies)
Jan 11 Javascript
解释&&和||在javascript中的另类用法
Jul 28 Javascript
js通过keyCode值判断单击键盘上某个键,然后触发指定的事件方法
Feb 19 Javascript
JavaScript解析任意形式的json树型结构展示
Jul 23 Javascript
js异步编程小技巧详解
Aug 14 Javascript
浅谈FastClick 填坑及源码解析
Mar 02 Javascript
微信小程序购物车、父子组件传值及calc的注意事项总结
Nov 14 Javascript
JS实现动态添加外部js、css到head标签的方法
Jun 05 Javascript
Vue父组件监听子组件生命周期
Sep 03 Javascript
Vue实现返回顶部按钮实例代码
Oct 21 Javascript
在Angular项目使用socket.io实现通信的方法
Jan 05 Javascript
原生js实现放大镜组件
Jan 22 Javascript
JS中的二叉树遍历详解
Mar 18 #Javascript
简述JavaScript提交表单的方式 (Using JavaScript Submit Form)
Mar 18 #Javascript
Javascript的表单验证-揭开正则表达式的面纱
Mar 18 #Javascript
Javascript的表单验证-初识正则表达式
Mar 18 #Javascript
Javascript的表单验证-提交表单
Mar 18 #Javascript
Javascript的表单与验证-非空验证
Mar 18 #Javascript
悬浮广告方法日常收集整理
Mar 18 #Javascript
You might like
PHP实现对数字分隔加千分号的方法
2019/03/18 PHP
thinkPHP+mysql+ajax实现的仿百度一下即时搜索效果详解
2019/07/15 PHP
实现JavaScript中继承的三种方式
2009/10/16 Javascript
jquery attr 设定src中含有&(宏)符号问题的解决方法
2011/07/26 Javascript
nodejs的require模块(文件模块/核心模块)及路径介绍
2013/01/14 NodeJs
jquery 跳到顶部和底部动画2句代码简单实现
2013/07/18 Javascript
鼠标滑在标题上显示图片的JS代码
2013/11/19 Javascript
FullCalendar日历插件应用之数据展现(一)
2015/12/23 Javascript
Javascript中的数组常用方法解析
2016/06/17 Javascript
js 动态添加元素(div、li、img等)及设置属性的方法
2016/07/19 Javascript
利用jquery实现验证输入的是否是数字、小数,包含保留几位小数
2016/12/07 Javascript
基于 Vue 的树形选择组件的示例代码
2017/08/18 Javascript
nodejs微信扫码支付功能实现
2018/02/17 NodeJs
详解Vue用cmd创建项目
2019/02/12 Javascript
vue router 通过路由来实现切换头部标题功能
2019/04/24 Javascript
JavaScript实现随机五位数验证码
2019/09/27 Javascript
JQuery实现折叠式菜单的详细代码
2020/06/03 jQuery
python简单实现计算过期时间的方法
2015/06/09 Python
Python 基础教程之闭包的使用方法
2017/09/29 Python
Python实现的寻找前5个默尼森数算法示例
2018/03/25 Python
Django中的Model操作表的实现
2018/07/24 Python
Python双向循环链表实现方法分析
2018/07/30 Python
用python实现英文字母和相应序数转换的方法
2019/09/18 Python
python批量将excel内容进行翻译写入功能
2019/10/10 Python
python+gdal+遥感图像拼接(mosaic)的实例
2020/03/10 Python
pycharm无法安装第三方库的问题及解决方法以scrapy为例(图解)
2020/05/09 Python
乐天旅游台湾网站:Rakuten Travel TW
2017/06/01 全球购物
优秀大学生求职自荐信范文
2014/04/19 职场文书
家庭贫困证明
2014/09/23 职场文书
2016年大学自主招生自荐信范文
2015/03/24 职场文书
预备党员考察意见范文
2015/06/01 职场文书
法制教育主题班会
2015/08/13 职场文书
小学语文教师研修感悟
2015/11/18 职场文书
Nginx域名转发使用场景代码实例
2021/03/31 Servers
海贼王十大潜力果实,路飞仅排第十,第一可毁世界(震震果实)
2022/03/18 日漫
【海涛DOTA解说】EVE女子战队独家录像加ZSMJ神牛两连发
2022/04/01 DOTA