jQuery原始碼剖析(四) - Deferred非同步回撥解決方案

極客James發表於2019-07-25

jQuery 原始碼解析程式碼及更多學習乾貨: 猛戳GitHub

本篇程式碼為 my-jQuery 1.0.4.js

建議閱讀本篇先弄懂上一篇Callbacks 原理分析,因為Deferred非同步回撥是基於Callbacks。下載原始碼然後根據文章思路學習,最好自己邊思考邊多敲幾遍。

一、基本概念

Promise/A+規範

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

jQuery原始碼剖析(四)  - Deferred非同步回撥解決方案

Promise作為一個模型,提供了一個在軟體工程中描述的延時概念的解決方案。

  • 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操作擴充套件到了所有操作,動畫、定時中也常用。

二、開始剖析 Deferred

1.Deferred 提供的API

(1) $.Deferred()生成一個deferred物件。

(2)deferred.done() 指定操作成功時的回撥函式

(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原始碼剖析(四)  - Deferred非同步回撥解決方案

其他

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

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

關注公眾號回覆:學習 領取前端最新最全學習資料,也可以進群和大佬一起學習交流

jQuery原始碼剖析(四)  - Deferred非同步回撥解決方案

相關文章