以前的時候,碰到過幾回同事問我,說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);
後一種明顯比第一種看起來要思路清晰,它避免了代碼的嵌套,看起來也更舒服一些。
------------------------------------------------------------------------------------------------------------------------------------------------------------
額,就這些了,看了個大概,不知有沒有錯誤的地方,,,請指出改正。