jQuery源碼剖析(四) - Deferred異步回調解決方案

jQuery 源碼解析代碼及更多學習乾貨: 猛戳GitHub前端

本篇代碼爲 my-jQuery 1.0.4.jsgit

建議閱讀本篇先弄懂上一篇Callbacks 原理分析,由於Deferred異步回調是基於Callbacks。下載源碼而後根據文章思路學習,最好本身邊思考邊多敲幾遍。es6

1、基本概念

Promise/A+規範

首先推薦各位閱讀一下 Promise/A+規範github

Promise做爲一個模型,提供了一個在軟件工程中描述的延時概念的解決方案。ajax

  • 1.Promise表示一個異步操做的最終結果
  • 2.與Promise最主要的交互方法是,經過將函數傳入他的then方法,從而得到Promise最終的值或Promise最終拒絕的值的緣由。
  • 3.一個Promise必須處於如下幾個狀態之中:pending,fulfilled,rejected.
  • 4.一個Promise必須提供一個then方法來獲取其值或緣由。

Promise和Deferred的關係

Promise是ES6提出的異步編程模式,能夠參考阮一峯ES6- Promise編程

Deferred是jQuery提出的回調函數解決方案,主要依賴Callbacks回調,能夠參考上一篇Callbacks原理解析設計模式

主要解決的問題是:當一個異步依賴於另外一個異步請求的結果時,或者某個操做須要等另外幾個操做都結束後纔開始等,更強大的是從ajax操做擴展到了全部操做,動畫、定時中也經常使用。數組

2、開始剖析 Deferred

1.Deferred 提供的API

(1) $.Deferred()生成一個deferred對象。promise

(2)deferred.done() 指定操做成功時的回調函數bash

(3) deferred.fail()指定操做失敗時的回調函數

(4) .promise()返回一個Promise對象來觀察當某種類型的全部行動綁定到結合,排隊與否仍是已經完成。

(5) deferred.resolve() 手動改變deferred對象的運行狀態爲"已完成",從而當即觸發done()方法。

(6)deferred.reject() 這個方法與deferred.resolve()正好相反,調用後將deferred對象的運行狀態變爲"已失敗",從而當即觸發fail()方法。

(7) $.when()爲多個操做指定回調函數。 除了這些方法之外,deferred對象還有二個重要方法,上面的教程中沒有涉及到。

(8)deferred.then() 有時爲了省事,能夠把done()fail()合在一塊兒寫,這就是then()方法。

(9)deferred.progress()當Deferred(延遲)對象生成時,調用已添加的處理程序。

2. jQuery封裝用法

// jQuery Deferred 寫法
var dtd = $.Deferred();// 新建一個Deferred對象
var wait = function(dtd){
    var tasks = function(){
        alert("執行完畢");
        dtd.resolve();//改變Deferred對象的執行狀態 成功狀態
    };
   setTimeout(tasks,2000);
   return dtd;
};
// 延遲對象的狀態 決定調用哪一個隊列的處理函數
$.when(wait(dtd))
.done(function(){
    alert("成功啦");
}).fail(function(){
    alert("出錯了");
})
//  dtd.resolve(); 改變dtd的執行狀態致使done馬上執行 
//   dtd.reject(); 改變dtd的執行狀態致使fail馬上執行 
// dtd.resolve();
複製代碼

以上代碼輸出:

執行完畢
成功啦

經過以上代碼,咱們反推jQuery的源碼是如何實現的。

3.源碼的設計思路:

如下僅爲核心代碼片斷,完整代碼請點擊源碼分析下載

(1)首先定義了tuples的數據結構,用來組裝存儲異步延遲三種不一樣狀態信息的描述.

/**
 *  tuples     定義一個數組來存儲三種不一樣狀態信息的描述
 *  第一個參數  延時對象的狀態
 *  第二個參數  往隊列裏添加處理函數
 *  第三個參數  建立不一樣狀態的隊列
 *  第四個參數  記錄最終狀態信息
 * **/
var tuples = [
  ["resolve","done",jQuery.Callbacks("once memory"),"resolved"],
  ["reject","fail",jQuery.Callbacks("once memory"),"rejected"],
  ["notify","progress",jQuery.Callbacks("memory")]]
複製代碼

(2)而後定義一個promise用來封裝state,then,promise對象

promise = {
    state :function(){
        return state;
    },
    then:function() {
    },
    promise:function(obj) {
        console.log(promise);
        debugger
        return obj !=null ? jQuery.extend(obj,promise):promise;
    }
    },
複製代碼


(3)定義一個延遲對象 deferred = {};
(4)遍歷封裝好的tuples數組隊列,把數組裏第二個元素也就是映射到Callbacks而且給到list,將數組裏第三個元素記錄最終狀態的信息給到stateString,而後把數組第一個元素即延時對象的狀態映射到Callbacks的add方法上,定義輔助方法deferred[resolveWith],deferred[rejectWith],deferred[notifyWith],最後調用Callbacks的fireWith方法實現隊列的回調。

// 遍歷 tuples
tuples.forEach(function(tuple,i){
var list = tuple[2], // 建立隊列  建立三次 self對象的引用 映射 調用Callbacks裏面的方法 
    stateString = tuple[3]; // 拿到當前最終信息的描述
// promise[done | fail |progress]  將這三個狀態都拿到Callbacks self裏面方法的引用 添加處理函數
promise[tuple[1]] = list.add;

// Handle state 成功或者失敗
if (stateString) { //添加第一個處理程序
    list.add(function(){
        // state = [resolved | rejected]
        state = stateString;
    });
}
// deferred [resolve | reject | notify ]  延時對象的狀態拿到函數的引用
deferred[tuple[0]] = function(){
    deferred[tuple[0] + "With"] (this === deferred ? promise : this, arguments);
    return this;
};
// list.fireWith 執行隊列而且傳參
// 調用隊列中的處理函數 而且給他們傳參 綁定執行時的上下文對象
deferred[tuple[0] + "With"] = list.fireWith;
});
複製代碼

(5)將deferred返回出去

// Make the deferred a promise
promise.promise(deferred);
return deferred;
複製代碼

(6)定義一個when方法

// 執行一個或多個對象的延遲函數的回調函數
when : function(subordinate){
    return subordinate.promise();
}
});
複製代碼

至此,大功告成,實現了jQuery的源碼剖析,體會了到做者的數據結構隊列處理編程思想

Deferred設計圖:

其餘

jQuery 源碼剖析 系列目錄地址:猛戳GitHub

jQuery 源碼剖析 系列預計寫十篇左右,旨在加深對原生JavaScript 部分知識點的理解和深刻,重點講解 jQuery核心功能函數、選擇器、Callback 原理、延時對象原理、事件綁定、jQuery體系結構、委託設計模式、dom操做、動畫隊列等。 若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。若是喜歡或者有所啓發,歡迎 star⭐️,對做者也是一種鼓勵。

關注公衆號回覆:學習 領取前端最新最全學習資料,也能夠進羣和大佬一塊兒學習交流

相關文章
相關標籤/搜索