四、异步队列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