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 相关文章推荐
javascript IE中的DOM ready应用技巧
Jul 23 Javascript
jQuery maxlength文本字数限制插件
Apr 16 Javascript
jquery蒙版控件实现代码
Dec 08 Javascript
javascript之typeof、instanceof操作符使用探讨
May 19 Javascript
javascript的内存管理详解
Aug 07 Javascript
DOM基础教程之使用DOM控制表格
Jan 20 Javascript
javascript将异步校验表单改写为同步表单
Jan 27 Javascript
JavaScript数据操作_浅谈原始值和引用值的操作本质
Aug 23 Javascript
VUE axios发送跨域请求需要注意的问题
Jul 06 Javascript
详解Node.js中的Async和Await函数
Feb 22 Javascript
详解Vue3.0 前的 TypeScript 最佳入门实践
Jun 18 Javascript
vant picker+popup 自定义三级联动案例
Nov 04 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中url传递中文字符,特殊危险字符的解决方法
2013/08/17 PHP
带密匙的php加密解密示例分享
2014/01/29 PHP
ThinkPHP添加更新标签的方法
2014/12/05 PHP
php有道翻译api调用方法实例
2014/12/22 PHP
记录一次排查PHP脚本执行卡住的问题
2016/12/27 PHP
Laravel 解决composer相关操作提示php相关异常的问题
2019/10/23 PHP
javascript  Error 对象 错误处理
2008/05/18 Javascript
javascript 基础篇2 数据类型,语句,函数
2012/03/14 Javascript
查看图片(前进后退)功能实现js代码
2013/04/24 Javascript
理解JAVASCRIPT中hasOwnProperty()的作用
2013/06/05 Javascript
form表单action提交的js部分与html部分
2014/01/07 Javascript
使用js显示当前时间示例
2014/03/02 Javascript
轻松创建nodejs服务器(7):阻塞操作的实现
2014/12/18 NodeJs
使用javascript获取页面名称
2014/12/23 Javascript
JavaScript检测浏览器cookie是否已经启动的方法
2015/02/27 Javascript
使用Object.defineProperty实现简单的js双向绑定
2016/04/15 Javascript
JavaScript30 一个月纯 JS 挑战中文指南(英文全集)
2017/07/23 Javascript
Vue2 配置 Axios api 接口调用文件的方法
2017/11/13 Javascript
js实现上传并压缩图片效果
2018/01/10 Javascript
详解ES6语法之可迭代协议和迭代器协议
2018/01/13 Javascript
分析javascript原型及原型链
2018/03/18 Javascript
30分钟快速入门掌握ES6/ES2015的核心内容(下)
2018/04/18 Javascript
vue resource发送请求的几种方式
2019/09/30 Javascript
在Vue 中获取下拉框的文本及选项值操作
2020/08/13 Javascript
[03:07]2015国际邀请赛选手档案EHOME.rOtK 是什么让他落泪?
2015/07/31 DOTA
Python算法之栈(stack)的实现
2014/08/18 Python
python语言元素知识点详解
2019/05/15 Python
Python使用PyQt5/PySide2编写一个极简的音乐播放器功能
2020/02/07 Python
基于Python pyecharts实现多种图例代码解析
2020/08/10 Python
python3 os进行嵌套操作的实例讲解
2020/11/19 Python
CSS代码检查工具stylelint的使用方法详解
2021/03/27 HTML / CSS
2014升学宴答谢词
2014/01/26 职场文书
法人委托书范本
2014/09/15 职场文书
法学专业大学生实习自我鉴定
2014/10/05 职场文书
贫困证明怎么写
2015/06/16 职场文书
python中redis包操作数据库的教程
2022/04/19 Python