以前的时候,碰到过几回同事问我,说js的同步怎么处理,就是我想先执行这段代码(耗时相对较长的一行,多数是异步的一些api调用),执行完了以后我再执行下边这句,每次我都很无奈的说不晓得,若是是ajax的话我知道async能够搞定,固然这并无什么卵用。近段时间在作一个自动化测试的项目,先后端分离的。旁边的同事要作前端websql的增删改查,一直在说jquery的deferred如何如何,我也不懂,就感受听上去高大上的一个东西,心里忐忑之余就找两篇文章读了一下,而后就有了此文。javascript
------------------------------------------------------------------------------------------------------------------------------------------------------------html
js如何执行的问题:js不是严格一行一行的执行的,它是一段一段的进行解析,这个解析过程主要是对声明的提早,剩余的基本上仍是按照前后顺序执行的。js是单线程的,正常状况下的确是先执行上一行而后下一行的,那不少异步的api是怎么实现的呢,执行队列。关于这里能够参考这篇文章:http://www.cnblogs.com/3body/p/5691744.html,关于同步的问题怎么实现呢,依然很差搞,但能够经过回调函数让它看起来是同步的。 前端
在回调函数方面,jquery的功能比较弱,为了改变这一点,jquery团队设计了deferred对象。简单来讲,deferred对象就是jquery的回调函数解决方案。在英语中,defer的意思是“延迟”,因此deferred对象的含义就是延迟到将来某个时间点再来执行。Deferred对象是jquery从1.5.0开始引入的,它完全改变了如何在jquery中使用ajax。为了实现它,jquery的所有ajax代码都被改写了。
jquery1.5以前的版本,ajax返回的是XHR(xmlHttpRequest)对象,能够从jquery的api中看到:java
从1.5开始,返回的仍是xhr对象,可是这个对象实现了deferred的接口方法,因此能够当成deferred使用,也所以,jquery的ajax能够有了deferred版本的写法:
原来咱们的ajax写法: jquery
$.ajax({ url: "test.html", success: function(){ alert("哈哈,成功了!"); }, error:function(){ alert("出错啦!"); } });
若是是1.5以后的jquery,能够这么写:web
$.ajax("test.html") .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); });
deferred对象究竟长啥样子,咱们能够看图:ajax
这里边有几个经常使用方法,resolve()表示将deferred的状态置为已解决也就是成功,reject()表示把状态置为失败,done()是执行成功的回调函数,fail是失败的回调函数,always()是不管如何都会执行的函数,相似于try...catch的finally,progress()是状态变为开始执行的回调函数,state()能够获取deferred对象的状态。
如何使用这个deferred对象呢?sql
一、其实前面的代码中已经使用了,$.ajax(url).done(function(){}).fail(function(){})这行代码中,$.ajax()返回了一个deferred对象,而后咱们链式的调用,使用了这个对象的done跟fail方法来设定了两个回调函数;
二、若是我要判断多个方法执行完成后再调用某个函数,怎么作呢?
这里要用到$.when()编程
$.when($.ajax("test1.html"), $.ajax("test2.html")) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); });
三、若是我不是ajax呢,本身手写的函数怎么作呢,让函数返回一个deferred对象便可:后端
function a(){ var d1 = $.Deferred(); d1.resolve("aa","bb",{"myaa":"xxxx"}); return d1; } function b(){ var d2 = $.Deferred(); d2.reject(); return d2; } $.when(a(),b()) .done(function(a,b,c){debugger;alert("success"+a);}) .fail(function(){alert("error");})
顺便咱们能够看到,deferred对象能够传参!只是要注意的是resolve方法里边的参数无论有几个,最后会以一个数组的形式传给回调函数,回调函数只有一个接收参数。上述代码只要a(),b()一个rejected则fail,将b修改成resolved,则执行结果以下:
能够看到只有a有值,其它两个参数都在回调函数第一个参数的两个数组元素中。
这里有个要注意的地方,就是$.when()只接收deferred对象,若是你传给when的不是deferred对象,会发现函数会立刻执行的,起不到回调的做用;
咱们很容易发现一个问题,deferred对象的状态是能够手动代码修改的,由于resolve()就能够置为成功,reject()就能够置为失败,在复杂代码环境下这个太危险了。所以有了promise对象。
我本地用的jquery-1.11.3,看上面deferred的结构图能够看到有个方法叫promise(),这个就是返回一个pormise对象。这个pormise对象跟deferred是很是相似的,差异就是promise对象去掉了reject()跟resolve()方法,也就是不对外提供修改状态的方法,省得多方调用引发混乱。其实jquery返回的就是个promise对象,咱们想改状态是改不了的。看一下ajax返回对象跟pormise的结构图:
图:ajax XHR
图:promise
咱们能够很清晰的看到这两个promise比deferred少了resolve,resolveWith,reject,rejectWith等这些改变对象状态的方法。promise不但愿咱们能手动更改promise对象的状态。由于:假设场景中,咱们返回promise对象给它人使用,这个对象被a跟b两个方法引用了,a方法改变了promise的状态,那么这会对b方法形成影响,这是很不安全的。
那么,deferred跟promise就是为了帮助咱们实现同步编程的吗?
你能够这么认为,其实咱们须要的只是一个回调而已。准确的说,deferred仅仅是一种回调方案而已,它可使咱们方便的书写异步代码,也可使得咱们的代码看上去更加优雅。
好比,一段动画的代码:
$('.animateEle').animate({ opacity:'.5' }, 4000,function(){ $('.animateEle2').animate({ width:'100px' },2000,function(){ // 这样太伤了 $('.animateEle3').animate({ height:'0' },2000); }); });
咱们能够这么写:
var animate1 = function() { return $('.animateEle1').animate({opacity:'.5'},4000).promise(); }; var animate2 = function() { return $('.animateEle2').animate({width:'100px'},2000).promise(); }; var animate3 = function(){ return $('.animateEle3').animate({height:'0'},2000).promise(); }; $.when(animate1()).then(animate2).then(animate3);
后一种明显比第一种看起来要思路清晰,它避免了代码的嵌套,看起来也更舒服一些。
------------------------------------------------------------------------------------------------------------------------------------------------------------
额,就这些了,看了个大概,不知有没有错误的地方,,,请指出改正。