javascript 學習筆記之JQuery中的Deferred對象

  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腳本的異步過程提供了一種結果處理方式,它不改變原有的異步過程,以更加簡潔、更加易讀的方式管理並執行異步操做,從原理上來講,只是更加完善的完成異步操做而已。

相關文章
相關標籤/搜索