javascript非同步代碼的回調地獄以及JQuery.deferred提供的promise解決方案

來源:互聯網
上載者:User

標籤:jquery.deferred使用方式   為什麼要使用jquery.deferre   改善javascript非同步代碼   回調地獄   

我們先來看一下編寫AJAX編碼經常遇到的幾個問題:

1.由於AJAX是非同步,所有依賴AJAX返回結果的代碼必需寫在AJAX回呼函數中。這就不可避免地形成了嵌套,ajax等非同步作業越多,嵌套層次就會越深,代碼可讀性就會越差。

$.ajax({    url: url,    data: dataObject,    success: function(){console.log("I depend on ajax result.");    },    error: function(){}});console.log("I will print before ajax finished.");

2.如果AJAX請求之間存在依賴關係,我們的代碼就會形成Pyramid of Doom(金字塔厄運)。比如我們要完成這樣一件事:有4個供Ajax訪問的url地址,需要先Ajax訪問第1個,在第1個訪問完成後,用拿到的返回資料作為參數再訪問第2個,第2個訪問完成後再第3個...以此到4個全部訪問完成。按照這樣的寫法,似乎會變成這樣:

$.ajax({    url: url1,    success: function(data){        $.ajax({            url: url2,            data: data,            success: function(data){                $.ajax({                    //...                });            }            });    }});

3.考慮這種情境,假如我們同時發送兩個Ajax請求,然後要在兩個請求都成功返回後再做一件接下來的事,想一想如果只按前面的方式在各自的調用位置去附加回調,這是不是很困難?


可以看到:javascript中類似於AJAX這種非同步操作,會導致代碼嵌套層次複雜,可讀性差,有的時候甚至是實現需求都非常困難。為瞭解決這種非同步回調難的問題,CommonJS組織制定了非同步模式編程規範Promises/A。目前該規範已經有了很多的實現者,比如Q, when.js, jQuery.Deffered()等。我們以jQuery.Deffered學習下Promise。


Promise的狀態

Promise對象有3種可能的狀態:肯定狀態(resolved)、否定狀態(rejected)、等待狀態(pending)。剛開始建立的Promise對象處於pending狀態,只能從pending變成resolved或者是從pending變成rejected狀態。

var df1 = $.Deferred(); console.log(df1.state());//pendingvar df2 = $.Deferred(); df2.resolve();//resolvedconsole.log(df2.state());var df3 = $.Deferred(); df3.reject();console.log(df3.state());//rejected
$.Deferred()建立一個延遲物件(也就是Promise對象),deferred.state()可以擷取Promise對象當前所處的狀態。deferred.resolve()和deferred.reject()則是用來改變Promise對象的狀態。

Promise添加回呼函數

Promise對象有3種狀態,我們可以分別為這3種狀態註冊回呼函數。當Promise處於某個狀態的時候,會觸發這個狀態下註冊的回呼函數。

var df = $.Deferred(); df.done(function(){alert("success");});df.fail(function(){alert("fail");});df.progress(function(){alert("progress");});df.notify();df.resolve();// df.reject();
done()、fail()、progress()分別註冊resolved、rejected、pending狀態下的回呼函數。通過resolve()、reject()、notify()可以觸發事先註冊的回呼函數。

Promise是支援鏈式調用的,上面的代碼可以寫成下面的樣子。

var df = $.Deferred(); df.done(function(){alert("success");}).fail(function(){alert("fail");}).progress(function(){alert("progress");});


Promise支援多個回呼函數,會按照註冊順序調用。

var df = $.Deferred(); df.done(function(){alert("first");}).fail(function(){alert("fail");});df.done(function(){alert("second");});df.done(function(){alert("third");});df.resolve();


deferred.always()添加的回呼函數,無論Promise是resolved狀態還是rejected狀態,都會被調用。

var df1 = $.Deferred(); df1.always(function(type){alert(type);});df1.resolve("resolve");var df2 = $.Deferred(); df2.always(function(type){alert(type);});df2.reject("reject");

progress()和notify()能夠用來實現進度條效果,因為notify()允許調用多次,而reject()和resolve()只能調用一次。這個很好理解,因為一旦狀態變成resolved或者是rejected,就不能再改變其狀態,也沒有必要。

var df = $.Deferred();   df.done(function(){alert("success");});  df.fail(function(){alert("fail");});  df.progress(function(){alert("progress");});    // resolve()調用2次,但是只能觸發1次successdf.resolve();  df.resolve();  var mudf = $.Deferred();   mudf.done(function(){alert("success");});  mudf.fail(function(){alert("fail");});  mudf.progress(function(){alert("progress");});    // 每次調用notify都會觸發progress回呼函數mudf.notify("%10");  mudf.notify("%20");  

rejectWith()、resolveWith()、notifyWith()功能上和reject()、resolve()、notify()沒有什麼差別,主要差別在於回呼函數中的執行內容(方法中的this)和參數形式。具體差別可以參考"JQuery.Callbacks系列一:api使用詳解"這篇文章中的fire()和fireWith()。


上面簡單的介紹了Promise的使用方式,我們可以用Promise的方式來編寫AJAX代碼。可以很容易地看出:使用Promise後代碼嵌套層次少了,代碼是縱向增長的,而不再是橫向增長。而且使用Promise,可以指定多個ajax回呼函數。

// 老的ajax寫法$.ajax({  url: "test.html",  success: function(){    alert("success");  },  error:function(){    alert("error");  }});// 使用promise後的寫法$.ajax("test.html") .done(function(){}) .fail(function(){}) .done(function(){) .fail(function(){);

JQuery中的Deferred對象與Promise對象區別

JQuery.Deferred相關的API,有的返回的是Deferred對象,有的返回的是Promise對象。如done()、reject()等大部分函數返回的都是Deferred對象,$.when()和then()函數返回的是Promise對象。具體可以參考JQuery API文檔。

JQuery官方對Promise Objects的解釋是:

This object provides a subset of the methods of the Deferred object (then, done, fail, always, progress, state and promise) to prevent users from changing the state of the Deferred.

可以看到Promise對象其實就是Deferred對象的一部分,Deferred對象提供了notify、reject、resolve等改變狀態的方法,但是Promise對象沒有提供這些方法。


文章開始提到的AJAX問題1~3,問題1可以很容易通過Promise得到解決。問題2和問題3是通過$.when()和deferred.then()得到解決,由於這2個API相對來說複雜一些,以後的文章再分析這2個API。


參考文章

"非同步JavaScript與Promise"作者ACGTOFE



著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

javascript非同步代碼的回調地獄以及JQuery.deferred提供的promise解決方案

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.