读jQuery源码之四(Callbacks,Deferred,when)-attach

看了下Sizzle部分源码,核心的原理就是使用正则表达式去匹配,找到对应的原生获取元素的方法,我没有去细究了。大家有兴趣可以自己看看,分享分享!

从2850行开始,继续往下读jQuery源码(2850-3043行)

进入Callbacks(回调函数管理模块)之前,有几个扩展方法

1.dir方法

三个参数:elem——dom元素,dir——指定elem的层级名称(例如parentNode,nextSibling),until——结束判断。返回一个数组,比如获取某个元素的parentNode,如果不指定until,则会返回一直到html的父级元素的数组。这里可以知道html的父级元素是document,document父级元素为null——没有父级元素。

 

2.pushStack:模拟入栈操作,生成了一个新的jQuery对象。

 

3.jQuery.Callbacks:回调函数的列表,管理函数模块

  行:3066-3228

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
        fired,
        // Flag to know if list is currently firing
        firing,
        // First callback to fire (used internally by add and fireWith)
        firingStart,
        // End of the loop when firing
        firingLength,
        // Index of currently firing callback (modified by remove if needed)
        firingIndex,
        // Actual callback list
        list = [],
        // Stack of fire calls for repeatable lists
        stack = !options.once && [],
        // Fire callbacks
        fire = function( data ) {
            memory = options.memory && data;
            fired = true;
            firingIndex = firingStart || 0;
            firingStart = 0;
            firingLength = list.length;
            firing = true;
            for ( ; list && firingIndex < firingLength; firingIndex++ ) {
                if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
                    memory = false; // To prevent further calls using add
                    break;
                }
            }
            firing = false;
            if ( list ) {
                if ( stack ) {
                    if ( stack.length ) {
                        fire( stack.shift() );
                    }
                } else if ( memory ) {
                    list = [];
                } else {
                    self.disable();
                }
            }
        },
        // Actual Callbacks object
        self = {
            // Add a callback or a collection of callbacks to the 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 );
                                }
                            } else if ( arg && arg.length && type !== "string" ) {
                                // Inspect recursively
                                add( arg );
                            }
                        });
                    })( arguments );
                    // Do we need to add the callbacks to the
                    // current firing batch?
                    if ( firing ) {
                        firingLength = list.length;
                    // With memory, if we're not firing then
                    // we should call right away
                    } else if ( memory ) {
                        firingStart = start;
                        fire( memory );
                    }
                }
                return this;
            },
            // Remove a callback from the 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
            fireWith: function( context, args ) {
                if ( list && ( !fired || stack ) ) {
                    args = args || [];
                    args = [ context, args.slice ? args.slice() : args ];
                    if ( firing ) {
                        stack.push( args );
                    } 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
            fired: function() {
                return !!fired;
            }
        };

    return self;
};

View Code

 3.1 参数options,把注释翻译如下:

  once:确保回调函数列表只触发一次,在fire上执行

  memory:记录上一次触发的参数,之后添加的回调函数都使用该参数值被立即调用,在add上执行,添加的时候会判断是否执行

  unique:同一个回调函数只能被添加一次,避免回调函数列表重复,在add上执行

  stopOnFalse:当其中一个回调函数返回false时中断执行回调函数,在fire上执行

3.2 var rnotwhite=(/\S+/g/):匹配一个非空格字符。

  

  这主要是将options字符串格式转换为对象格式并且缓存(optionsCache)

  例如”once memory”通过createOptions(“once memory”)==>{“once”:”true”,”memory”:”true”}

3.3 主要包含的两个函数fire和self,Callbacks内部定义了一个list数组来存放回调函数列表

  3.3.1 fire:触发所有回调函数执行,通过list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) 这个来执行函数,两个参数分别为:上下文和函数参数。

  3.3.2 self:对回调函数list进行add,remove等操作

 

4.行3231-3461 Deferred和when(异步队列模块)

4.1 Deferred:相对于Callbacks,有3个函数列表,分别代表成功,失败,消息回调。

        var tuples = [
                // action, add listener, listener list, final state
                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
                [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
                [ "notify", "progress", jQuery.Callbacks("memory") ]
            ],
...
jQuery.each( tuples, function( i, tuple ) {
            var list = tuple[ 2 ],
                stateString = tuple[ 3 ];

            // promise[ done | fail | progress ] = list.add
            promise[ tuple[1] ] = list.add;

通过遍历tuples来生成了3个函数列表,并且以Add方法的形式添加到了promise里面。promise是一个异步队列只读的副本,只负责添加和判断,不更改状态和触发执行。在promise里面还定义了一个同名的promise方法,负责返回一个promise副本,then是一种快捷方式——在一个方法里面的添加3个函数列表(成功,失败,消息)。接着给promise添加了一个pipe属性指向then。在创建了deferred的只读副本以后,后面开始给deferred对象添加修改状态和触发执行的方法。

4.2 when:针对deferred的再一次封装,顾名思义,当所有的成功回调函数执行完毕时,然后再做什么事情。根据一个计数标志,每一次执行了成功回调函数以后就–1,当计算器==0的时候,就执行。如图:

4.3 最后会返回一个主异步队列对象return deferred.promise();

待续…

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注