Deffered是Jquery中的一个很是重要的对象,从1.5版本以后,Jquery中的ajax操做都基于Deffered进行了重构,这个对象的处理模式就像其余Javascript框中的Promise异步模式同样,它表明一个潜在的、长时间运行但没必要返回完成操做的结果,与等待并阻塞浏览器进程直到完成操做相比,Deffered返回的是一个承诺异步执行结果的对象,这个承诺能够有返回值,也能够没有,浏览器被释放出来作其余事情,直到这个返回结果被使用到。Deffered的原理是给异步请求过程当中状态的变化注册回调函数,实现链式调用,如对象的then函数;统一对这些回调函数的结果进行管理控制,以面对多个请求的须要,如Jquery中的when函数。html
下面用一个简单的例子看看Deffered对象是如何工做的:jquery
1 function BeginRequest() { 2 //定义deferred对象 3 var def = $.Deferred(); 4 //建立XMLHttpRequest对象 5 var request = new XMLHttpRequest(); 6 //配置一个异步请求 7 request.open("GET", "../textPage/httpScript.ashx"); 8 //定义请求状态变化的处理过程 9 request.onreadystatechange = function() { 10 if (request.readyState === 4 && request.status === 200) { 11 def.resolve(request.responseText); 12 } 13 else if (request.status === 404) { 14 def.reject(); 15 } 16 } 17 //设置请求头 18 request.setRequestHeader("content-type", "text/plain"); 19 //开启一个请求 20 request.send(null); 21 //返回deferred对象 22 return def; 23 }
咱们先经过JQuery中的Deferred()函数建立一个deferred对象,而后在异步请求中根据请求的状态分别注册对象的resolve和reject函数,最后返回这个deferred对象,接下来能够进行链式调用:ajax
1 BeginRequest().then(function(s) { 2 console.log("success."); 3 }, function() { 4 console.log("failed."); 5 });
上面调用了deferred对象的then函数,这个函数有两个函数参数,第一个是异步结果成功的时候执行,第二个是失败的时候执行。数组
这里须要介绍一下deferred对象的三种执行状态:未完成、已完成、已失败。若是是已完成(resolve),deferred对象会当即触发done()函数指定的回调函数;若是是已失败(reject),deferred对象会当即触发fail()函数指定的回调函数;若是是未完成,即等待过程当中,则继续等待或者deferred对象触发process()(jquery1.7版本及以上)函数指定的回调函数。上面的then函数传递了两个回调函数分别对完成和失败两种状态进行处理,并且deferred对象的状态能够经过调用对象中resolve()和reject()函数改变,调用分别触发done()和fail()指定的回调函数,故上述调用部分能够改写为:promise
1 BeginRequest().done(function(s) { 2 console.log("success."); 3 }).fail(function() { 4 console.log("failed."); 5 });
能够看出,deferred对象的引入给程序的异步执行带来了极大的便利,更加简洁,可读性更高。这在JQuery Ajax请求中获得了及广泛的应用,在1.5版本之前,Ajax请求返回的是XHR对象,其成功及差错处理方式以下:浏览器
1 $.ajax({ 2 //要请求的url 3 url: "../textPage/httpScript.ashx", 4 //请求成功后调用的回调函数 5 success: function(result) { 6 console.log("success."); 7 }, 8 //请求失败后调用的回调函数 9 error: function(e) { 10 console.log("failed."); 11 } 12 });
传统的ajax请求接收一个无类型对象为参数,对象中指定了请求的url及回调函数,当回调过程当中嵌套ajax请求的话,这个过程看起来很不清晰。在1.5及之后的版本中,ajax返回的对象再也不是XHR类型,而是deferred对象,且会自动触发其状态的改变,故新的ajax请求能够改写为:安全
1 $.ajax("../textPage/httpScript.ashxs").then(function() { 2 console.log("success."); 3 }, function() { 4 console.log("failed."); 5 });
这里使用了then方法,固然,也能够调用fail和done函数实现。异步
上面对JQuery中deferred对象的使用有了初步的了解,接下来进行进一步学习:函数
deferred对象的做用范围学习
正是由于deferred的状态能够经过其对象调用resolve和reject函数来动态改变,因此在程序流程中咱们不但愿其状态被意外的改变而形成错误,下面代码是很好的证实:
1 function BeginRequest() { 2 //定义deferred对象 3 var def = $.Deferred(); 4 //建立XMLHttpRequest对象 5 var request = new XMLHttpRequest(); 6 //配置一个异步请求 7 request.open("GET", "../textPage/httpScript.ashx"); 8 //定义请求状态变化的处理过程 9 request.onreadystatechange = function() { 10 if (request.readyState === 4 && request.status === 200) { 11 //正常处理过程被延时5秒钟 12 setTimeout(function() { 13 def.resolve("the request is complete."); 14 }, 5000); 15 } 16 else if (request.status === 404) { 17 def.reject(); 18 } 19 } 20 //设置请求头 21 request.setRequestHeader("content-type", "text/plain"); 22 //开启一个请求 23 request.send(null); 24 //返回deferred对象 25 return def; 26 } 27 28 //保存异步操做结果的deferred对象,以做他用 29 var tempDef = BeginRequest().done( 30 function(msg) { 31 if (arguments.length <= 0) { 32 //若是是意外改变 33 console.log("valid call."); 34 } 35 else { 36 //成功后改变 37 console.log(msg); 38 } 39 }).fail( 40 function() { 41 console.log("failed."); 42 }); 43 //意外改变deferred的状态为已完成 44 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为例,代码以下:
1 $.ajax("../textPage/httpScript.ashxs") 2 .done(function() { 3 console.log("success."); 4 }) 5 .done(function() { 6 console.log("第二次处理."); 7 }) 8 .done(function() { 9 console.log("第三次处理"); 10 }); 11 //……
大部分实际需求均不会如此使用,但对于流程性比较强的处理过程,能够如此来分步执行,使实现过程更加简洁,特别是在动画处理的过程当中。
大多数时候,咱们只须要在done的回调函数中执行响应处理操做便可,可是偶尔须要针对不一样的状况做出不一样的处理,如何作呢?嗯,其实done等异步处理函数中指定的回调函数是能够添加参数的,单个异步请求中,回调函数有三个参数
,第一个参数是异步结果,第二个对象是请求状态,第三个是结果的deferred对象,代码以下:
1 $.ajax("../textPage/httpScript.ashx") 2 //成功时执行此回调函数 3 .done( 4 function(result, status, def) { 5 console.log(result.toString()); 6 console.log(status.toString()); 7 console.log(typeof def); 8 } 9 ) 10 //失败时执行此回调函数 11 .fail( 12 function(result, status, def) { 13 console.log("failed."); 14 } 15 );
输出以下:
为多个异步操做指定统一的回调处理
在实际需求中,咱们会遇到不少异步操做,若是业务逻辑要求在某些异步操做均完成后再进行其余操做,那么咱们须要对这些异步操做进行等待,直到全部都完成,即为多个异步操做定义统一的回调函数来对成功和失败进行管理,传统的异步方法是作不到的。在JQuery中,when函数能够很好的很方便的解决这一问题,来看下面的代码:
1 $.when($.ajax("../textPage/httpScript.ashx"), $.ajax("../textPage/Pictrue.aspx")) 2 //成功时执行此回调函数 3 .done( 4 function() { 5 console.log("success.") 6 } 7 ) 8 //失败时执行此回调函数 9 .fail( 10 function() { 11 console.log("failed."); 12 } 13 );
$.when()函数返回的是deferred对象,故上面对该返回值用函数done()、fail()分别处理成功及失败的状况(then函数也能够实现),和其余不一样的是when函数中咱们执行了两个异步请求,它的执行过程须等待全部起步请求执行完毕,才能进行done或者fail的处理流程,而且,只有当when函数中的全部异步操做均成功才能触发done函数中指定的回调函数,其中一个或者多个失败则会触发fail函数中指定的回调函数。
有趣的是,咱们能够在done或者fail中进行详细跟踪每一个请求的结果,这须要对done和fail中定义的回调函数添加参数,改写以下:
1 $.when($.ajax("../textPage/httpScript.ashx"), $.ajax("../textPage/Pictrue.aspx")) 2 //成功时执行此回调函数 3 .done( 4 function(r1, r2) { 5 console.log("success.") 6 } 7 ) 8 //失败时执行此回调函数 9 .fail( 10 function(r1, r2) { 11 console.log("failed."); 12 } 13 );
通过测试,发现上述阴影部分所标记的参数分别表明when中每一个请求的结果对象,对这些参数进行监控调试,其内容以下:
由上图能够看出,r1和r2均是长度为3的数组,以r1为例,数组的第一个元素是异步操做的结果,第二个元素是异步操做状态,第三个元素是当前异步操做返回的deferred结果对象。经验证,r1和r2分别是then中两个请求的结果对象,如此咱们在为多个异步操做统一回调函数的同时,也能够根据不一样操做的返回结果给用户以不一样的反馈,这能够经过给回调函数添加参数得到。
不局限于异步ajax请求
JQuery中的deferred对象不只应用于ajax异步请求,对于一些复杂耗时的脚本操做也是能够的,它提供了对异步操做结果的一个未来结果,用以对异步操做的一个管理,查看下面的 代码:
1 var handle = function() { 2 //建立deferred对象 3 var def = $.Deferred(); 4 //内部函数 5 function test() { 6 alert("over."); 7 //改变执行状态 8 def.resolve(); 9 } 10 //模仿某个复杂耗时的操做,模拟须要5秒,而后再执行函数test 11 setTimeout("test()", 5000); 12 //返回deferred对象; 13 return def.promise(); 14 } 15 16 $.when(handle) 17 .done( 18 function() { 19 console.log("success."); 20 } 21 ) 22 .fail( 23 function() { 24 console.log("failed."); 25 } 26 );
总结:JQuery中的deferred对象为了js脚本的异步过程提供了一种结果处理方式,它不改变原有的异步过程,以更加简洁、更加易读的方式管理并执行异步操做,从原理上来讲,只是更加完善的完成异步操做而已。