Deferred跟promise跟js同步問題記錄

  以前的時候,碰到過幾回同事問我,說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);

  後一種明顯比第一種看起來要思路清晰,它避免了代碼的嵌套,看起來也更舒服一些。

  

------------------------------------------------------------------------------------------------------------------------------------------------------------

  額,就這些了,看了個大概,不知有沒有錯誤的地方,,,請指出改正。

相關文章
相關標籤/搜索