1  在开发的过程中,我们经常遇到某些耗时很长的javascript操作,并且伴随着大量的异步。

  2  比如我们有一个ajax的操作,这个ajax从发出请求到接收响应需要5秒,在这5秒内我们可以运行其他代码段,当响应到达后,我们需要判断响应的结果(无非就是成功或者失败),并根据不同的结果  添加回调函数

  3  为了有效的简洁的添加回调函数jQuery引入了Callbacks。

  4  而为了方便的 根据不同的结果(或者根据各种跟结果有关的逻辑,比如不管是成功或者失败) 添加回调函数,jQuery引入了Deferred。

 $.ajax("test.html")
  .done(function(){ alert("success"); })
  .fail(function(){ alert("error"); });

  5  因而Deferred与Callbacks是密不可分的,事实上,Callbacks也是从Deferred中分离出去的

回顾Callbacks

  1  Callbacks大体架构

深入理解jQuery中的Deferred

  2 Callbacks源码分析:

  define([
    "./core",
    "./var/rnotwhite"
], function( jQuery, rnotwhite ) {

// String to Object options format cache
var optionsCache = {};

// Convert String-formatted options into Object-formatted ones and store in cache
/*
如果: var a = $.Callback('once memory')
则 optionsCache中会有这么一项:"once memory":{memory:true,once:true}
*/
function createOptions( options ) {
    var object = optionsCache[ options ] = {};
    jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
        object[ flag ] = true;
    });
    return object;
}

/*
 * 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 ) {

    // Convert options from String-formatted to Object-formatted if needed
    // (we check in cache first)
    options = typeof options === "string" ?
        ( optionsCache[ options ] || createOptions( options ) ) :
        jQuery.extend( {}, options );

    var // Last fire value (for non-forgettable lists)
        memory,
        // Flag to know if list was already fired  list是否已经被fire函数调用过
        fired,
        // Flag to know if list is currently firing  当前是否正在调用fire函数
        firing,
        // First callback to fire (used internally by add and fireWith)  第一个被执行的回调函数在list的位置
        firingStart,
        // End of the loop when firing   fire函数要运行的回调函数的个数
        firingLength,
        // Index of currently firing callback (modified by remove if needed)  当前正在执行的回调函数的索引
        firingIndex,
        //回调函数数组
        list = [],
        // Stack of fire calls for repeatable lists  可重复的回调函数栈。我们可能会短时间内执行多次fire(),若当前的fire()正在迭代执行回调函数,而紧接着又执行了一次fire()时,会将下一次的fire()参数等保存至stack中,等待当前的fire()执行完成后,将stack中的fire()进行执行
        stack = !options.once && [],
        // Fire callbacks
        fire = function( data ) {
            // data[0] 是一个对象,data[1]则是回调函数的参数
            memory = options.memory && data;  // 很精妙,仔细体会一下这句代码,如果调用Calbacks时传入了memory,则memory = data,否则memory = false
            fired = true; // 在调用本函数时,将fired状态进行修改
            firingIndex = firingStart || 0;
            firingStart = 0;
            firingLength = list.length;
            firing = true; // 迭代回调函数之前,将firing状态进行修改
            for ( ; list && firingIndex < firingLength; firingIndex++ ) {
                if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false &&
                    options.stopOnFalse ) { // 运行回调函数的同时,检测回调函数是否返回false,若返回false,且调用Callbacks时传入stopOnFalse参数,则终止迭代

                    memory = false; // To prevent further calls using add 既然终止迭代了,那么之后添加的回调函数都不应该被调用,将memory设置为false
                    break;
                }
            }
            firing = false;  // 迭代回调函数完成后,将firing状态进行修改
            if ( list ) {
                if ( stack ) {  // 没有使用once参数
                    if ( stack.length ) {
                        fire( stack.shift() );
                    }
                } else if ( memory ) { // 使用了once memory参数,则在迭代完回调函数之后清空list
                    list = [];
                } else { // 其他
                    self.disable();
                }
            }
        },
        // Actual Callbacks object
        self = {
            // 将一个新的回调函数添加至list
            add: function() {
                if ( list ) {
                    // First, we save the current length  首先,我们将当前的长度进行保存
                    var start = list.length;
                    (function add( args ) {  // 自执行函数
                        jQuery.each( args, function( _, arg ) {
                            var type = jQuery.type( arg );
                            if ( type === "function" ) {
                                if ( !options.unique || !self.has( arg ) ) {
                                    list.push( arg ); // 若参数中的元素为函数且(无unique参数或者list中没有该函数),则将该函数添加至list末尾
                                }
                            } else if ( arg && arg.length && type !== "string" ) { //  arg的长度不为0且每项的类型不为字符串,也就是args为这种情况:[[fun1,fun2...],[fun3,fun4]](不仅限于这种情况)
                                // Inspect recursively
                                add( arg );
                            }
                        });
                    })( arguments );
                    // Do we need to add the callbacks to the
                    // current firing batch?
                    // 当Callback中的firingLength变为 动态的! 也就是:只要我们向list中添加了一个新的回调函数,即使在fire()运行过程中,改变也能立即体现出来
                    if ( firing ) {
                        firingLength = list.length;
                    // With memory, if we're not firing then
                    // we should call right away
                    } else if ( memory ) { // 如果当前没有执行回调函数,且存在memory参数,则执行新添加的回调函数
                        firingStart = start;
                        fire( memory );
                    }
                }
                return this;
            },
            // Remove a callback from the list 将一个回调函数从list中移除
            remove: function() {
                if ( list ) {
                    jQuery.each( arguments, function( _, arg ) {
                        var index;
                        while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                            list.splice( index, 1 );
                            // Handle firing indexes
                            if ( firing ) {
                                if ( index <= firingLength ) {
                                    firingLength--;
                                }
                                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 && list.length );
            },
            // Remove all callbacks from the list 清空数组
            empty: function() {
                list = [];
                firingLength = 0;
                return this;
            },
            // Have the list do nothing anymore  使用了这个方法,则意味着该回调对象失效了。
            disable: function() {
                list = stack = memory = undefined;
                return this;
            },
            // Is it disabled?
            disabled: function() {
                return !list;
            },
            // Lock the list in its current state 给数组上锁
            lock: function() {
                stack = undefined;
                if ( !memory ) {
                    self.disable();
                }
                return this;
            },
            // Is it locked?
            locked: function() {
                return !stack;
            },
            // Call all callbacks with the given context and arguments
            // 使用传入的context作为当前函数的执行上下文
            fireWith: function( context, args ) {
                if ( list && ( !fired || stack ) ) {
                    args = args || [];
                    args = [ context, args.slice ? args.slice() : args ];
                    if ( firing ) {
                        stack.push( args ); // 如果当前正在迭代执行回调函数,则将新的fire参数推入stack中
                    } else {
                        fire( args );
                    }
                }
                return this;
            },
            // Call all the callbacks with the given arguments
            fire: function() {
                self.fireWith( this, arguments );
                return this;
            },
            // To know if the callbacks have already been called at least once
            // 用来确定当前callback对象是否被fire()过
            fired: function() {
                return !!fired;
            }
        };

    return self;
};

return jQuery;
});
源码讲解

相关文章:

  • 2022-01-19
  • 2022-12-23
  • 2021-11-08
  • 2021-08-01
  • 2022-12-23
  • 2021-12-25
  • 2022-01-20
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-02-20
  • 2021-09-12
  • 2021-12-10
  • 2021-10-22
相关资源
相似解决方案