Deffered是Jquery中的一个非常重要的对象,从1.5版本之后,Jquery中的ajax操作都基于Deffered进行了重构,这个对象的处理模式就像其他Javascript框中的Promise异步模式一样,它代表一个潜在的、长时间运行但不必返回完成操作的结果,与等待并阻塞浏览器进程直到完成操作相比,Deffered返回的是一个承诺异步执行结果的对象,这个承诺可以有返回值,也可以没有,浏览器被释放出来做其他事情,直到这个返回结果被使用到。Deffered的原理是给异步请求过程中状态的变化注册回调函数,实现链式调用,如对象的then函数;统一对这些回调函数的结果进行管理控制,以面对多个请求的需要,如Jquery中的when函数。

下面用一个简单的例子看看Deffered对象是如何工作的:

 function BeginRequest() {
//定义deferred对象
var def = $.Deferred();
//创建XMLHttpRequest对象
var request = new XMLHttpRequest();
//配置一个异步请求
request.open("GET", "../textPage/httpScript.ashx");
//定义请求状态变化的处理过程
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200) {
def.resolve(request.responseText);
}
else if (request.status === 404) {
def.reject();
}
}
//设置请求头
request.setRequestHeader("content-type", "text/plain");
//开启一个请求
request.send(null);
//返回deferred对象
return def;
}

我们先通过JQuery中的Deferred()函数创建一个deferred对象,然后在异步请求中根据请求的状态分别注册对象的resolve和reject函数,最后返回这个deferred对象,接下来可以进行链式调用:

 BeginRequest().then(function(s) {
console.log("success.");
}, function() {
console.log("failed.");
});

上面调用了deferred对象的then函数,这个函数有两个函数参数,第一个是异步结果成功的时候执行,第二个是失败的时候执行。

  这里需要介绍一下deferred对象的三种执行状态:未完成、已完成、已失败。如果是已完成(resolve),deferred对象会立即触发done()函数指定的回调函数;如果是已失败(reject),deferred对象会立即触发fail()函数指定的回调函数;如果是未完成,即等待过程中,则继续等待或者deferred对象触发process()(jquery1.7版本及以上)函数指定的回调函数。上面的then函数传递了两个回调函数分别对完成和失败两种状态进行处理,而且deferred对象的状态可以通过调用对象中resolve()和reject()函数改变,调用分别触发done()和fail()指定的回调函数,故上述调用部分可以改写为:

 BeginRequest().done(function(s) {
console.log("success.");
}).fail(function() {
console.log("failed.");
});

可以看出,deferred对象的引入给程序的异步执行带来了极大的便利,更加简洁,可读性更高。这在JQuery Ajax请求中得到了及普遍的应用,在1.5版本以前,Ajax请求返回的是XHR对象,其成功及差错处理方式如下:

 $.ajax({
//要请求的url
url: "../textPage/httpScript.ashx",
//请求成功后调用的回调函数
success: function(result) {
console.log("success.");
},
//请求失败后调用的回调函数
error: function(e) {
console.log("failed.");
}
});

传统的ajax请求接收一个无类型对象为参数,对象中指定了请求的url及回调函数,当回调过程中嵌套ajax请求的话,这个过程看起来很不清晰。在1.5及以后的版本中,ajax返回的对象不再是XHR类型,而是deferred对象,且会自动触发其状态的改变,故新的ajax请求可以改写为:

 $.ajax("../textPage/httpScript.ashxs").then(function() {
console.log("success.");
}, function() {
console.log("failed.");
});

这里使用了then方法,当然,也可以调用fail和done函数实现。

上面对JQuery中deferred对象的使用有了初步的了解,接下来进行进一步学习:

deferred对象的作用范围

正是因为deferred的状态可以通过其对象调用resolve和reject函数来动态改变,所以在程序流程中我们不希望其状态被意外的改变而造成错误,下面代码是很好的证明:

 function BeginRequest() {
//定义deferred对象
var def = $.Deferred();
//创建XMLHttpRequest对象
var request = new XMLHttpRequest();
//配置一个异步请求
request.open("GET", "../textPage/httpScript.ashx");
//定义请求状态变化的处理过程
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200) {
//正常处理过程被延时5秒钟
setTimeout(function() {
def.resolve("the request is complete.");
}, 5000);
}
else if (request.status === 404) {
def.reject();
}
}
//设置请求头
request.setRequestHeader("content-type", "text/plain");
//开启一个请求
request.send(null);
//返回deferred对象
return def;
} //保存异步操作结果的deferred对象,以作他用
var tempDef = BeginRequest().done(
function(msg) {
if (arguments.length <= 0) {
//如果是意外改变
console.log("valid call.");
}
else {
//成功后改变
console.log(msg);
}
}).fail(
function() {
console.log("failed.");
});
//意外改变deferred的状态为已完成
tempDef.resolve();

  代码中将请求成功后的执行过程人为的延迟了5秒,以模仿耗时操作,在调用的时候保存了deferred对象以备他用,但是程序意外的通过这个被保存的对象更改了其状态,执行上面的代码,输出”valid call“而不是”the request is complete .“。

可见,deferred对象的直接传递存在一定的安全和不确定的风险,为了避免这种情况,有三种方式可以解决:

1. 尽量在同一个函数或者模块中使用deferred对象及其相关操作。

2. 在使用deferred对象时尽量使用链式调用,不要暴露传递中的deferred对象给其他模块。

3. 为此,JQuery专门为deferred定义了一个promise方法,该方法也返回一个deferred对象,但这个对象不再包含引起状态改变的函数,如:resolve、reject、resolveWith等,这种方式最优。

对于JQuery中的Promise函数,我的理解是为了保持异步执行状态,防止不确定状态改变而定义的。借上例,我们将BeginRequest()函数的最后一行改写为:

1 //返回deferred的安全对象
2 return def.promise();

如此,再次执行上面的过程,程序果断抛出异常:

5秒后输出正常路径下的结果,可见,deferred对象的状态没有被改变。这里有一篇很好的文章,可以参考一下。

同一个异步操作指定多个回调函数

也就是说,在异步操作返回的deferred对象上可以绑定多个回调函数(done、fail、then均可),幸运的是:done、fail、then函数返回的都是对同一个deferred对象的引用,故可以对同一个deferred对象执行多个done(或者fail或者then)处理过程,这些处理过程一次顺序执行,以done为例,代码如下:

 $.ajax("../textPage/httpScript.ashxs")
.done(function() {
console.log("success.");
})
.done(function() {
console.log("第二次处理.");
})
.done(function() {
console.log("第三次处理");
});
//……

大部分实际需求均不会如此使用,但对于流程性比较强的处理过程,可以如此来分步执行,使实现过程更加简洁,特别是在动画处理的过程中。

  大多数时候,我们只需要在done的回调函数中执行响应处理操作即可,但是偶尔需要针对不同的情况作出不同的处理,如何做呢?嗯,其实done等异步处理函数中指定的回调函数是可以添加参数的,单个异步请求中,回调函数有三个参数

,第一个参数是异步结果,第二个对象是请求状态,第三个是结果的deferred对象,代码如下:

 $.ajax("../textPage/httpScript.ashx")
//成功时执行此回调函数
.done(
function(result, status, def) {
console.log(result.toString());
console.log(status.toString());
console.log(typeof def);
}
)
//失败时执行此回调函数
.fail(
function(result, status, def) {
console.log("failed.");
}
);

输出如下:

为多个异步操作指定统一的回调处理

  在实际需求中,我们会遇到很多异步操作,如果业务逻辑要求在某些异步操作均完成后再进行其他操作,那么我们需要对这些异步操作进行等待,直到所有都完成,即为多个异步操作定义统一的回调函数来对成功和失败进行管理,传统的异步方法是做不到的。在JQuery中,when函数可以很好的很方便的解决这一问题,来看下面的代码:

 $.when($.ajax("../textPage/httpScript.ashx"), $.ajax("../textPage/Pictrue.aspx"))
//成功时执行此回调函数
.done(
function() {
console.log("success.")
}
)
//失败时执行此回调函数
.fail(
function() {
console.log("failed.");
}
);

  $.when()函数返回的是deferred对象,故上面对该返回值用函数done()、fail()分别处理成功及失败的情况(then函数也可以实现),和其他不同的是when函数中我们执行了两个异步请求,它的执行过程须等待所有起步请求执行完毕,才能进行done或者fail的处理流程,并且,只有当when函数中的所有异步操作均成功才能触发done函数中指定的回调函数,其中一个或者多个失败则会触发fail函数中指定的回调函数。

有趣的是,我们可以在done或者fail中进行详细跟踪每个请求的结果,这需要对done和fail中定义的回调函数添加参数,改写如下:

 $.when($.ajax("../textPage/httpScript.ashx"), $.ajax("../textPage/Pictrue.aspx"))
//成功时执行此回调函数
.done(
function(r1, r2) {
console.log("success.")
}
)
//失败时执行此回调函数
.fail(
function(r1, r2) {
console.log("failed.");
}
);

经过测试,发现上述阴影部分所标记的参数分别代表when中每个请求的结果对象,对这些参数进行监控调试,其内容如下:

  由上图可以看出,r1和r2均是长度为3的数组,以r1为例,数组的第一个元素是异步操作的结果,第二个元素是异步操作状态,第三个元素是当前异步操作返回的deferred结果对象。经验证,r1和r2分别是then中两个请求的结果对象,如此我们在为多个异步操作统一回调函数的同时,也可以根据不同操作的返回结果给用户以不同的反馈,这可以通过给回调函数添加参数获得。

不局限于异步ajax请求

JQuery中的deferred对象不仅应用于ajax异步请求,对于一些复杂耗时的脚本操作也是可以的,它提供了对异步操作结果的一个将来结果,用以对异步操作的一个管理,查看下面的 代码:

 var handle = function() {
//创建deferred对象
var def = $.Deferred();
//内部函数
function test() {
alert("over.");
//改变执行状态
def.resolve();
}
//模仿某个复杂耗时的操作,模拟需要5秒,然后再执行函数test
setTimeout("test()", 5000);
//返回deferred对象;
return def.promise();
} $.when(handle)
.done(
function() {
console.log("success.");
}
)
.fail(
function() {
console.log("failed.");
}
);

总结:JQuery中的deferred对象为了js脚本的异步过程提供了一种结果处理方式,它不改变原有的异步过程,以更加简洁、更加易读的方式管理并执行异步操作,从原理上来说,只是更加完善的完成异步操作而已。

javascript 学习笔记之JQuery中的Deferred对象的更多相关文章

  1. jQuery学习笔记(二)jQuery中DOM操作

    目录 DOM操作分类 jQuery中的各种DOM操作 查找节点 创建节点 删除节点 复制节点 替换节点 包裹节点 属性操作 样式操作 对HTML.文本和值的操作 遍历节点 CSS-DOM操作 小结 本 ...

  2. jQuery中的deferred对象和extend方法

    1⃣️deferred对象 deferred对象是jQuery的回调函数解决方案,它是从jQuery1.5.0版本开始引入的功能 deferred对象的方法 (1) $.Deferred() 生成一个 ...

  3. MongoDB学习笔记~MongoDB实体中的值对象

    回到目录 注意,这里说的值对象是指在MongoDB实体类中的,并不是DDD中的值对象,不过,两者也是联系,就是它是对类的补充,自己本身没有存在的价值,而在值对象中,也是不需要有主键Id的,这与DDD也 ...

  4. JavaScript学习笔记(四)——jQuery插件开发与发布

    jQuery插件就是以jQuery库为基础衍生出来的库,jQuery插件的好处是封装功能,提高了代码的复用性,加快了开发速度,现在网络上开源的jQuery插件非常多,随着版本的不停迭代越来越稳定好用, ...

  5. 【学习笔记】jQuery中的动画与效果

    1.基本效果 匹配元素从左上角开始变浓变大或缩小到左上角变淡变小 ①隐藏元素 除了可以设置匹配元素的display:none外,可以用以下函数 hide(speed,[callback])  返回值: ...

  6. jQuery学习笔记(9)--jquery中的事件--$(document).ready()

    1.$(document).ready()方法是事件模块中最重要的一个函数,可以极大地提高web应用程序的相应速度. 2.执行时机:DOM就绪后就会执行,而javascript中window.onlo ...

  7. web前端学习(四)JavaScript学习笔记部分(4)-- JavaScriptDOM对象

    1.Javascript-DOM简介 1.1.HTML DOM 1.2.DOM操作HTML 1.2.1.JavaScript能够改变页面中的所有HTML元素 1.2.2.JavaScript能够改变页 ...

  8. javascript学习笔记(八):浏览器对象

    window对象 <!DOCTYPE html> <html> <head lang="en"> <meta chaset="U ...

  9. 廖雪峰 JavaScript 学习笔记(字符串、数组和对象)

    字符串 1.和python一样,也是用' '或" "括起来的字符表示.但多行字符串是用反引号(esc下键)``,与之相对的是Python用''' '''三引号表示: 2.转义字符: ...

随机推荐

  1. python-面向对象(一)——开篇基础

    面向对象编程(Object Oriented Programming,OOP,面向对象程序设计) 一.创建类和对象 面向对象编程是一种编程方式,此编程方式的落地需要使用 “类” 和 “对象” 来实现, ...

  2. 1109 html5 xhtml;

    XHTML 是 XML 风格的 HTML 4.01. HTML5 是下一代 HTML,取代 HTML 4.01. XHTML是基于XML发布的HTML规范,旨在规范HTML的格式. 两者提出的目的是不 ...

  3. 中文乱码问题(使用Servlet3.0新特性实现文件上传——上传文件名中文乱码问题)

    问题描述:就是文件传送过来的文件名等是乱码 解决方法:将传送的JSP页面(即含有表单的页面)的页面编码方式改为: <%@ page contentType="text/html; ch ...

  4. android85 短信防火墙

    系统收到短信是有广播的,广播中包含了短信的号码和内容 ###短信防火墙 * 系统发送短信广播时,是怎么把短信内容存入广播的,我们就只能怎么取出来 * 如果短信过长,那么发送时会拆分成多条短信发送,那么 ...

  5. linux 系统调优2

    换作Linux:  1.杀使用内存大,非必要的进程 2.增加连接数 3.磁盘分区的碎片整理 4.服务优化,把不要的服务关闭 5.更换性能更好的硬件,纵向升级 常见优化手段: 1.更换性能更好的硬件,纵 ...

  6. careercup-数组和字符串1.7

    1.7 编写一个算法,若M*N矩阵中某个元素为0,则将其所在的行与列清零. 类似于leetcode中的 Set Matrix Zeroes C++实现代码: #include<iostream& ...

  7. 一个基于MVVM的TableView组件化实现方案

    AITableView https://github.com/chentoo/AITableView cocoapods: pod ‘AITableView’ 做什么用? 这是一个简化UITableV ...

  8. [Eclipse]The type XXX cannot be resolved. It is indirectly referenced from required .class files

    在Eclipse中遇到The type XXX cannot be resolved. It is indirectly referenced from required .class files错误 ...

  9. NDK-r7以上版本部署方法

    一.关于NDK: NDK全称:Native Development Kit. 1.NDK是一系列工具的集合. NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和jav ...

  10. Java基础知识强化之网络编程笔记01:InetAddress类的概述和使用

    1. InetAddress类 InetAddress是Java对IP地址的封装,在java.net中有许多类都使用到了InetAddress,包括ServerSocket,Socket,Datagr ...