四、異步隊列Deferred Object1——jQuery.Callbacks(options)


在回調函數列表內部,通過一個數組來保存回調函數,其他方法則圍繞這個數組進行操作和檢測。
回調函數列表支持添加、移除、觸發、鎖定和禁用回調函數,為jQuery.ajax()、jQuery.Deferred()和ready事件提供基礎功能,我們也可以基於它編寫新得組件。
// 代碼行 3190——3201
var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );



// Convert String-formatted options into Object-formatted ones
// 工具函數,將字符串格式得標記轉換為對象格式得標記。
function createOptions( options ) {
    var object = {};
    // 對象object用於存儲轉換后得標記對象,其屬性為標記字符串,屬性值為true。
    jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
        // 用空白符吧標記字符串分割為數組,然后遍歷數組,為返回值object,添加單個標記,屬性值一律為true。
        object[ flag ] = true;
    } );
    // 最后返回對象格式得標記
    return object;
}

// 代碼行:3203——3417
/*
 * Create a callback list using the following parameters:
 *
 *    options: an optional list of space-separated options that will change how
 *            the callback list behaves or a more traditional option object
 *
 * By default a callback list will act like an event callback list and can be
 * "fired" multiple times.
 *
 * Possible options:
 *
 *    once:            will ensure the callback list can only be fired once (like a Deferred)
 *
 *    memory:            will keep track of previous values and will call any callback added
 *                    after the list has been fired right away with the latest "memorized"
 *                    values (like a Deferred)
 *
 *    unique:            will ensure a callback can only be added once (no duplicate in the list)
 *
 *    stopOnFalse:    interrupt callings when a callback returns false
 *
 */
jQuery.Callbacks = function( options ) {

    // 參數options是可選得字符串,用以改變回調函數列表得行為。如果不傳入參數options,則回調函數列表得行為類似於事件監聽函數,能夠被觸發多次;如果傳入參數options,則參數值可以是單個標記或多個標記得組合,多個標記之間用空格隔開。
    
    // Convert options from String-formatted to Object-formatted if needed
    // (we check in cache first)
    // 解析字符串標記options為對象
    // 如果options是字符串,調用工具函數createOptions(options)將標記字符串options解析為標記對象,否則調用jquery.extend()將options中的內容合並到一個空對象中
    options = typeof options === "string" ?
        createOptions( options ) :
        jQuery.extend( {}, options );

        // 聲明局部變量,通過閉包機制引用
    var // Flag to know if list is currently firing
    // 標記,以知道回調函數列表是否正在執行中
        firing,

        // Last fire value for non-forgettable lists
        // memory得可能值和用途有些復雜,在分析工具fire(context,args)時詳細分析
        memory,

        // Flag to know if list was already fired
        // 標記,以知道list是否已被觸發
        fired,

        // Flag to prevent firing
        // 標記,防止firing
        locked,

        // Actual callback list
        // 存放回調函數得數組
        list = [],

        // Queue of execution data for repeatable lists
        // 在可重復觸發、正在執行得列表上,重復觸發時,將上下文和參數放入數組queue中
        queue = [],

        // Index of currently firing callback (modified by add/remove as needed)
        // 當前正在執行得回調函數得下標
        firingIndex = -1,

        // Fire callbacks
        // 實際觸發回調函數得工具函數
        // 工具函數fire()調用數組list中得回調函數。該函數通過閉包機制引用數組list
        fire = function() {

            // Enforce single-firing
            locked = locked || options.once;

            // Execute callbacks for all pending executions,
            // respecting firingIndex overrides and runtime changes
            fired = firing = true;
            for ( ; queue.length; firingIndex = -1 ) {
                // 變量memory的初始值為undefined,表示當前回調函數列表未被觸發過。
                // 在memory模式下,變量memory的值為[context,args],間接地表示回調函數列表已經被觸發過。
                // 在非memory模式下,變量memory的值為true,間接地表示回調函數列表已經被觸發過;如果同時是once模式,則不再執行任何回調函數。
                // 在stopOnFalse模式下,如果某個回調函數返回了false,則變量memory的值為true,間接地表示回調函數列表已經被觸發過。
                memory = queue.shift();
                while ( ++firingIndex < list.length ) {

                    // Run callback and check for early termination
                    // 執行回調函數list[firingIndex],如果返回值是false,並且當前回調函數列表是stopOnFalse模式,則變量memory則賦值為false,並且停止執行后續得其他回調函數,也間接表示了當前回調函數列表已經被觸發過。
                    if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
                        options.stopOnFalse ) {

                        // Jump to end and forget the data so .add doesn't re-fire
                        firingIndex = list.length;
                        memory = false;
                    }
                }
            }

            // Forget the data if we're done with it
            if ( !options.memory ) {
                memory = false;
            }

            firing = false;

            // Clean up if we're done firing for good
            if ( locked ) {

                // Keep an empty list if we have data for future add calls
                // 如果我們有用於將來添加調用的數據,請保留一個空列表
                if ( memory ) {
                    list = [];

                // Otherwise, this object is spent
                // 否則,此對象將被使用
                } else {
                    list = "";
                }
            }
        },

        // Actual Callbacks object
        // 回調函數列表,方法jQuery.callbacks(flags)得返回值。
        self = {

            // Add a callback or a collection of callbacks to the list
            // 添加一個或一組回調函數到回調函數列表中
            add: function() {
                if ( list ) {

                    // If we have memory from a past run, we should fire after adding
                    // 如果我們有過去運行時的內存,則應該在添加之后觸發
                    if ( memory && !firing ) {
                        // 在開始添加回調函數前,先備份數組list的長度,該值也是回調函數的插入位置
                        firingIndex = list.length - 1;
                        queue.push( memory );
                    }

                    // 調用工具函數add()添加回調函數
                    ( function add( args ) {
                        jQuery.each( args, function( _, arg ) {
                            if ( isFunction( arg ) ) {
                                if ( !options.unique || !self.has( arg ) ) {
                                    list.push( arg );
                                }
                            } else if ( arg && arg.length && toType( arg ) !== "string" ) {

                                // Inspect recursively
                                add( arg );
                            }
                        } );
                    } )( arguments );

                    // 在memory模式下,如果回調函數列表未在執行中,並且已經被觸發過,則修正起始下標為回調函數的插入位置,然后調用工具函數fire()立即執行添加新的回調函數。
                    if ( memory && !firing ) {
                        fire();
                    }
                }
                // 返回當前回調函數列表,以保持鏈式語法
                return this;
            },

            // Remove a callback from the list
            // 從回調函數列表中移除一個或一組回調函數
            // 方法callbacks.remove()用於從回調函數列表中移除一個或一組回調函數,移除前修正當前下標firingIndex,確保移除的同時不會漏執行回調函數。
            remove: function() {
                // 遍歷待移除的回調函數數組,在循環體內嵌套遍歷已有的回調函數數組,如果檢查到某個待移除回調函數與某個已有的回調函數完全相等,則從已有的回調函數數組中移除它。
                jQuery.each( arguments, function( _, arg ) {
                    var index;
                    while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                        // 調用數組原型方法splice(),從匹配下標處開始移除1個元素,
                        list.splice( index, 1 );

                        // Handle firing indexes
                        // 如果待移除函數的下標小於正在執行回調函數的下標firingIndex,即待移除的回調函數已經執行,則修正firingIndex減1,以確保不會漏執行回調函數。
                        if ( index <= firingIndex ) {
                            firingIndex--;
                        }
                    }
                } );
                // 返回當前回調函數列表,以保持鏈式語法
                return this;
            },

            // Check if a given callback is in the list.
            // If no argument is given, return whether or not list has callbacks attached.
            // 檢查指定得回調函數是否在回調函數列表中
            has: function( fn ) {
                return fn ?
                    jQuery.inArray( fn, list ) > -1 :
                    list.length > 0;
            },

            // Remove all callbacks from the list
            // 清空列表,移除回調函數列表中得所有回調函數
            empty: function() {
                if ( list ) {
                    list = [];
                }
                return this;
            },

            // Disable .fire and .add
            // Abort any current/pending executions
            // Clear all callbacks and values
            // 禁用回調函數列表,不在做任何事情
            disable: function() {
                // 設置變量locked、queue為空數組,list,memory為“”
                // 在方法callbacks.add()、callback.remove()中,如果變量list可以轉換為false,則忽略本次調用,即無法添加、移除回調函數;在方法callback.fireWith()中,如果變量locked的狀態可以轉換為false,則忽略本次調用,如果可以轉換為true,才有可能觸發回調函數。
                locked = queue = [];
                list = memory = "";
                return this;
            },
            // 判斷回調函數列表是否已被禁用
            disabled: function() {
                return !list;
            },

            // Disable .fire
            // Also disable .add unless we have memory (since it would have no effect)
            // Abort any pending executions
            // 鎖定回調函數列表
            lock: function() {
                locked = queue = [];
                if ( !memory && !firing ) {
                    list = memory = "";
                }
                return this;
            },
            // 判斷回調函數列表是否已被鎖定
            locked: function() {
                return !!locked;
            },

            // Call all callbacks with the given context and arguments
            // 使用指定得上下文和參數觸發回調函數列表中得所有回調函數
            fireWith: function( context, args ) {
                // 先檢測變量locked的狀態,如果可以轉換為false,則忽略本次調用,如果可以轉換為true,才有可能觸發回調函數。
                if ( !locked ) {
                    // 如果未傳入args,則使用默認值[]
                    args = args || [];
                    args = [ context, args.slice ? args.slice() : args ];
                    // 在可重復觸發、正在執行得列表上,重復觸發時,將上下文和參數放入數組queue中
                    queue.push( args );

                    // 回調函數列表不正在執行中,則調用工具函數fire()執行所有回調函數
                    if ( !firing ) {
                        fire();
                    }
                }
                return this;
            },

            // Call all the callbacks with the given arguments
            // 使用指定得參數調用回調函數列表中得所有回調函數
            fire: function() {
                // 方法callbacks.fire()通過調用callbacks.fireWith(context,args)實現,調用時指定上下文為當前回調函數列表。
                self.fireWith( this, arguments );
                return this;
            },

            // To know if the callbacks have already been called at least once
            // 判斷回調函數列表是否至少執行過一次
            fired: function() {
                // 方法callbacks.fired()使用邏輯非運算符(!)對變量fired兩次取反,轉換未布爾值並返回。
                return !!fired;
            }
        };

        // 返回回調函數列表self
    return self;
};

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM