jQuery源碼分析(九) 非同步隊列模塊 Deferred 詳解

来源:https://www.cnblogs.com/greatdesert/archive/2019/10/12/11454359.html
-Advertisement-
Play Games

deferred對象就是jQuery的回調函數解決方案,它解決瞭如何處理耗時操作的問題,比如一些Ajax操作,動畫操作等。(P.s:緊跟上一節:https://www.cnblogs.com/greatdesert/p/11433365.html的內容) 非同步隊列有三種狀態:待定(pending)、 ...


deferred對象就是jQuery的回調函數解決方案,它解決瞭如何處理耗時操作的問題,比如一些Ajax操作,動畫操作等。(P.s:緊跟上一節:https://www.cnblogs.com/greatdesert/p/11433365.html的內容)

非同步隊列有三種狀態:待定(pending)、成功(resolved)和失敗(rejected),初始時處於pending狀態

我們可以使用jQuery.Deferred創建一個非同步隊列,返回一個對象,該對象含有如下操作:

  • done(fn/arr)                     ;添加成功回調函數,當非同步隊列處於成功狀態時被調用,參數同:jQuery.Callbacks(flags).add(fn/arr)
  • fail(fn/arr)                           ;添加失敗回調函數,參數同上
  • progress(fn/arr)                         ;添加消息回調函數,參數同上
  • then(donefn/arr,failfn/arr,profn/arr)     ;同時添加成功回調函數、失敗回調函數和消息回調函數
  • always(fn/arr)                       ;添加回調函數到doneList和failList中,即保存兩份引用,當非同步隊列處於成功或失敗狀態時被調用    ;參數可以是函數、函數列表
  • state()                                  ;返回非同步隊列的當前狀態、返回pending、resolved或者rejected
  • promise(obj)                           ;如果設置了obj參數對象則為obj對象增加非同步隊列的方法並返回。如果未設置參數obj,則返回當前Deferred對象的只讀副本

如果調用$.Diferred()創建一個非同步隊列,返回後的對象可以調用如下幾個方法:

 writer by:大沙漠 QQ:22969969

  • resolve(args)                      ;使用指定的參數調用所有的成功回調函數,非同步隊列進入成功狀態,之後就無法再調用
  • reject(args)                           ;使用指定的參數調用所有的失敗回調函數,非同步隊列進入失敗狀態
  • notify(args)                              ;使用指定的參數調用所有的消息回調函數
  • resolveWith(context,args)               ;使用指定的上下文和參數調用所有的成功回調函數,非同步隊列進入成功狀態
  • rejectWith(context,args)               ;使用指定的上下文和參數調用所有的失敗回調函數,非同步隊列進入失敗狀態
  • notifyWith(context,args)               ;使用指定的上下文和參數調用所有的消息回調函數

是不是和上一節介紹的Callbacks的fire和fireWith很像,Defferred內部就是通過通過Callback()回調函數列表來實現的,例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.7.1/jquery.js"></script>
</head>
<body>
<script>
    function f1(x){console.log('f1 '+x);}
    function f2(x){console.log('f2 '+x);}
    function f3(x){console.log('f3 '+x);}

    var t = $.Deferred();                   //創建非同步隊列
    t.done(f1).fail(f2).progress(f3);       //添加成功回調函數、失敗回調函數和消息列表回調函數。等同於t.then(f1,f2,f3); 
    t.notify('test');                       //觸發所有消息函數列表,輸出:f3 test
    t.resolve('done');                      //觸發所有成功回調函數,輸出:done
    t.reject('fail');                       //沒有輸出,觸發成功回調函數後失敗回調函數列表就會被禁用,反過來也如此
    t.progress(f1);                         //輸出:f1 test。這是因為消息回調函數之前已經被調用過,保存了上下文和參數了,如果之前沒有調用,這裡就沒有輸出。
</script>
</body>
</html>

輸出如下:

 

源碼分析


非同步隊列內部的實現原理就是通過jQuery.Callbacks定義三個回調函數列表,分別存儲成功、失敗、消息回調函數,然後返回一個對象,在該對象上設置done、fail和progress,分別對應這三個回調函數列表的add方法,這樣就實現分別添加不同狀態的回調函數了

$.Deferred是通過jQuery.extend函數直接掛載到jQuery里的,結構如下:

jQuery.extend({

    Deferred: function( func ) {
        var doneList = jQuery.Callbacks( "once memory" ),            //成功回調函數列表
            failList = jQuery.Callbacks( "once memory" ),            //失敗回調函數列表
            progressList = jQuery.Callbacks( "memory" ),             //消息回調函數列表  ;這三個回調函數列表就是存儲我們添加的觸發函數的
            state = "pending",                                       //初始狀態:pending
            lists = {                                                //後面的方法會遍歷變數list中的屬性名來為非同步隊列添加方法deffred.resolve()、resolveWith()、reject()、rejectWith()、notify()和notifyWith()
                resolve: doneList,
                reject: failList,
                notify: progressList
            },
            promise = {                                                //非同步隊列的只讀副本
                /**/
            },
            deferred = promise.promise({}),                                //非同步隊列
            key;

        for ( key in lists ) {                                            //添加觸發成功、失敗、消息回調函數的方法,執行後deferred對象就多了resolve()、resolveWith()、reject()、rejectWith()、notify()和notifyWith()方法。
            deferred[ key ] = lists[ key ].fire;
            deferred[ key + "With" ] = lists[ key ].fireWith;
        }

        // Handle state
        deferred.done( function() {                                        //添加三個成功回調函數,分別用於設置狀態為成功(resolved),禁用失敗列表函數列表和鎖定消息回調函數列表
            state = "resolved";
        }, failList.disable, progressList.lock ).fail( function() {        //添加三個失敗回調韓素,分別用於設置狀態為失敗(rejected),禁用成功列表函數列表和鎖定消息回調函數列表
            state = "rejected";
        }, doneList.disable, progressList.lock );

        // Call given func if any
        if ( func ) {
            func.call( deferred, deferred );
        }

        // All done!
        return deferred;                                                //返回非同步隊列deffrred
    },
    //...
})

可以看到$.Deferred最後返回的是deferred這個佈局變數,而deferred是promise.promise({})的返回值,我們來看看promise的實現方法:

promise = {                        //非同步隊列的只讀副本
    done: doneList.add,                            //添加成功回調函數,引用對應的回調函數列表的callbacks.add(fn/arr)方法,下同
    fail: failList.add,                            //添加失敗回調函數
    progress: progressList.add,                    //添加消息回調函數

    state: function() {                            //返回非同步隊列的當前狀態:pending、resolved、rejected之一
        return state;
    },

    // Deprecated
    isResolved: doneList.fired,
    isRejected: failList.fired,

    then: function( doneCallbacks, failCallbacks, progressCallbacks ) {    //同時添加成功回調函數、失敗回調函數和消息回調函數
        deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
        return this;
    },
    always: function() {                                                //添加回調函數到doneList和failList中,即保存兩份引用,當非同步隊列處於成功或失敗狀態時被調用
        deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
        return this;
    },
    pipe: function( fnDone, fnFail, fnProgress ) {                        //過濾當前非同步隊列的狀態和參數,並返回一個新的非同步隊列的副本,一般用不到
        return jQuery.Deferred(function( newDefer ) {
            jQuery.each( {
                done: [ fnDone, "resolve" ],
                fail: [ fnFail, "reject" ],
                progress: [ fnProgress, "notify" ]
            }, function( handler, data ) {
                var fn = data[ 0 ],
                    action = data[ 1 ],
                    returned;
                if ( jQuery.isFunction( fn ) ) {
                    deferred[ handler ](function() {
                        returned = fn.apply( this, arguments );
                        if ( returned && jQuery.isFunction( returned.promise ) ) {
                            returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
                        } else {
                            newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
                        }
                    });
                } else {
                    deferred[ handler ]( newDefer[ action ] );
                }
            });
        }).promise();
    },
    // Get a promise for this deferred
    // If obj is provided, the promise aspect is added to the object
    promise: function( obj ) {                            //如果設置了obj參數對象則為obj對象增加非同步隊列的方法並返回。如果未設置參數obj,則返回當前Deferred對象的只讀副本,
        if ( obj == null ) {                                //如果obj為空
            obj = promise;                                        //則返回promise對象(是上一層作用域鏈的promise對象)
        } else {
            for ( var key in promise ) {                    //否則遍歷promise
                obj[ key ] = promise[ key ];                    //將promise的所有方法、屬性保存到obj中
            }
        }
        return obj;                                            //最後返回obj
    }
},

$.Deferred就是對Callbacks回調函數列表的管理而已,產生了一個新的$.Defferred介面,內部添加了一個state表示非同步隊列的狀態。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 概述 移動端所說的AI,通常是指“機器學習”。 定義:機器學習其實就是研究電腦怎樣模擬人類的學習行為,以獲取新的知識或技能,並重新組織已有的知識結構使之不斷改善自身。從實踐的意義上來說,機器學習是一類從數據中自動分析獲得規律,並利用規律對未知數據進行預測的演算法。 目前,機器學習已經有了十分廣泛的應 ...
  • 可實現多種漸變、直角or弧角、進度條、載入條 (Various gradient, right or arc angle, progress bar and loading bar can be realized) Github地址 YangsBryant/BGradualProgress (Git ...
  • 單選滾動選擇器、diy豐富、有阻尼效果、簡單美觀、觸摸or點擊模式 (Rolling Selector, Diy Rich, Damping Effect, Simple and Beautiful, Touch or Click Mode) Github地址 YangsBryant/DSelect ...
  • EditText搜索結果下拉框、自動or回調模式、可diy、使用超簡便 (EditText search results drop-down box, auto or callback mode, diy, easy to use)#支持自動展示搜索條目 #支持手動展示搜索條目(可自己記錄歷史數據, ...
  • 前言 項目開發中,多少會遇到這種需求:獲得設備唯一標識DeviceId,用於: 1.標識一個唯一的設備,做數據精準下發或者數據統計分析; 2.賬號與設備綁定; 3..... 分析 這類文章,網上有許多資料,例如:使用IMEI、MAC等作為設備標識使用。 不過,看過這些文章或者深入調研的同學應該都清楚 ...
  • 圖形報表很常用,因為展示數據比較直觀,常見的形式有很多,如:折線圖、柱形圖、餅圖、雷達圖、股票圖、還有一些3D效果的圖表等。 Android中也有不少第三方圖表庫,但是很難相容各種各樣的需求。 如果第三方庫不能滿足我們的需要,那麼就需要自己去寫這麼一個控制項。 往往在APP需求給定後,很多開發者卻無從 ...
  • 更多文章請參考:https://www.jianshu.com/u/5c628d7c8392 ...
  • 註:前提是你已經安裝好jdk和android-sdk,並且將兩者的環境變數已經設置完畢,我這裡是jdk8 獲取開發版sha1(也稱作測試版) 1.打開命令行 2.輸入keytool -list -v -keystore debug.keystore獲取開發板sha1 這裡應該讓你輸入密碼,我的是直接 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...