前端編程提高之旅(九)----延遲物件,之旅----
近日做內部推薦項目,在進入項目首頁時,需要進行預調用介面判斷,按照以往的習慣,應該將調用介面後執行的代碼,放入ajax請求success內執行。這樣當然可以,但是程式碼群組織和可讀性都沒有那麼高。通過查看Jquery官網api發現了deferred object(延遲物件)這個概念。
一、API文檔的翻譯:
延遲物件,在jquery1.5被引入,是調用jQuery.Deferred()方法產生的一個鏈式功用對象。它可以註冊多個回調到回調隊列,提取回調隊列,傳遞任何非同步或同步函數的成功或失敗狀態。
延遲物件是一個鏈式的,類似於jquery對象的鏈式,但是它有自己的方法。在建立之後,你可以用任何下面涉及到的方法,無論是直接鏈式調用,還是儲存這個對象到變數,並在這個變數上調用一個或多個方法。
添加操作方法,不管延遲物件是處於解決狀態還是拒絕狀態都被調用。
添加操作方法,只有延遲物件處於已解決狀態才被調用
添加操作方法,只有延遲物件處於被拒絕狀態才會被調用
判斷一個延遲物件是否處於被拒絕狀態
判斷一個延遲物件是否處於已解決狀態。
調用一個給定變數的延遲物件上的進行中的回調。
調用一個給定變數和內容相關的延遲物件上的進行中的回調。
用來過濾and或or鏈式延遲的效用方法
當延遲物件產生進行中通知時,添加操作被調用。
返回延遲允諾對象。
拒絕一個延遲物件並且調用任何給定參數的失敗回調。
拒絕回調對象,調用任何給定上下文和參數的失敗回調。
解決一個延遲物件,並且調用任何給定參數的完成回調。
解決一個延遲物件,並且調用任何給定上下文和參數的完成回調。
判斷當前延遲物件狀態。
當延遲物件被解決、拒絕或進行中添加方法被調用。
延遲物件的建構函式。
提供一個方法執行基於一個或多個對象的回呼函數,回呼函數經常是代表非同步事件的延遲物件
當所有的綁定到集合、隊列或沒有的動作被完成返回一個promise對象。
二、延遲物件分析與使用:
不難得出結論,以上API包括了延遲物件的構建、延遲狀態設定方法、延遲狀態監聽方法。
延遲物件的資料結構
延遲物件包括三種狀態:resolved(已解決)、rejected(未解決)、progress(進行中)。這三種狀態可以用延遲物件state方法即時查看。
延遲物件狀態查看
延遲物件是jquery的回呼函數解決方案,延遲顧名思義就是延遲到未來某個點再執行,類似jquery 動畫函數中delay方法,區別在於後者是延遲固定時間,延遲物件則是通過固定狀態判斷順延強制的時機。
var dtd = $.Deferred(); // 建立一個deferred對象 var wait = function(dtd){ var tasks = function(){ alert("執行完畢!"); dtd.resolve(); // 改變deferred對象的執行狀態 }; setTimeout(tasks,5000); return dtd; };$.when(wait(dtd)) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出錯啦!"); });
上述代碼執行流程圖:
上述代碼例子,延遲物件定義為一個全域對象,這樣就會造成延遲狀態,可以在任何時間變更。
改進的方法一:
var dtd = $.Deferred(); // 建立一個Deferred對象 var wait = function(dtd){ var tasks = function(){ alert("執行完畢!"); dtd.resolve(); // 改變Deferred對象的執行狀態 }; setTimeout(tasks,5000); return dtd.promise(); // 返回promise對象,promise對象只開放與狀態無關的方法,即那三種狀態方法不開放,無法設定 }; var d = wait(dtd); // 建立一個d對象,改為對這個對象進行操作 $.when(d) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出錯啦!"); }); d.resolve(); // 此時,這個語句是無效的
上述代碼,在wait函數返回值是一個promise對象,由promise對象資料結構不難發現,promise對象擁有除了修改狀態方法以外延遲物件的所有方法,也就是說,promise對象無法改變執行狀態,這樣就能夠防止外界對狀態的更改。promise對象的資料結構
改進方法二:
將延遲物件構建成函數內部的局部變數,這樣更好的實現了封裝,防止外部對狀態進行改變。
var wait = function(dtd){ var dtd = $.Deferred(); //在函數內部,建立一個Deferred對象 var tasks = function(){ alert("執行完畢!"); dtd.resolve(); // 改變Deferred對象的執行狀態 }; setTimeout(tasks,5000); return dtd.promise(); // 返回promise對象 }; $.when(wait()) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出錯啦!"); });
改進方法三:
直接將函數名作為$.Deferred()參數傳入,$.Deferred()所產生的延遲物件會作為wait函數參數傳入函數。
var wait = function(dtd){ var tasks = function(){ alert("執行完畢!"); dtd.resolve(); // 改變Deferred對象的執行狀態 }; setTimeout(tasks,5000); return dtd.promise(); // 返回promise對象 }; $.Deferred(wait) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出錯啦!"); });
$.Deferred()方式執行狀態
三、項目使用方式:
樂帝在內推項目中使用的是第二種方法,即將延遲物件作為局部變數定義,並返回promise對象。這裡promise對象與延遲物件除了不能改變狀態,對延遲狀態的記錄及監聽方法都相同。
function loadInternalHost() { var dtd = $.Deferred(); dtd.resolve(); return dtd.promise();//在原defferred對象上返回另一個deferred對象,兩個對象都會記錄最初deferred對象狀態,但後者不能改變狀態,其他方法一致 }
promise對延遲狀態的記錄
當分別調用兩個介面時,延遲監聽函數允許為多個事件指定一個回調。
如下代碼,分別取得內推城市及內推公告兩部分資料後,執行後續載入資料的動作。
$.when(getInternalRecommendCitys(), getComment()).done(function () { getInternalRecommendJobAdList(oldSearchData.keyWord, oldSearchData.locId); });function getInternalRecommendCitys() { var dtd = $.Deferred(); $.ajax({ type: "get", data: { "openId": openId }, dataType: "json", url: "../api/InternalInfo/InternalRecommendCitys", success: function (data) { $city = $("#citySelect"); for (var i = 1; i < data.length; i++) { $str = $("<option></option>"); $str.text(data[i].name); $str.val(data[i].value); $city.append($str); dtd.resolve(); }//將可選城市匯入到挑選清單 }, error: function () { dtd.resolve(); } }); return dtd.promise(); } function getComment() { var dtd = $.Deferred(); $.ajax({ type: "get", data: { "openId": openId }, dataType: "json", url: "../api/InternalInfo/GetComment", success: function (data) { $(".notice-content pre").html(data.data); dtd.resolve(); }, error: function () { dtd.resolve(); } });//擷取公告資料 return dtd.promise(); }