jquery 1.7.2源码解析(四) 异步队列Deferred Object

异步队列Deferred Object

一)jQuery.Callbacks( flags )

1.总体结构

 该函数返回一个链式工具对象(回调函数列表),用于管理一组回调函数。

 

2.源码分析

1.工具函数createFlags(flags)

该函数用于将字符串标记转换为对象格式标记,并把转换结果缓存起来。

// String to Object flags format cache
var flagsCache = {};

// Convert String-formatted flags into Object-formatted ones and store in cache
function createFlags( flags ) {
    var object = flagsCache[ flags ] = {},
        i, length;
    flags = flags.split( /\s+/ );
    for ( i = 0, length = flags.length; i < length; i++ ) {
        object[ flags[i] ] = true;
    }
    return object;
}

 2.工具函数add( args )

jQuery.Callbacks = function( flags ) {
    // Convert flags from String-formatted to Object-formatted
    // (we check in cache first)
    flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
    var // Actual callback list
        list = [],
        // Add one or several callbacks to the list
        add = function( args ) {
            var i,
                length,
                elem,
                type,
                actual;
            for ( i = 0, length = args.length; i < length; i++ ) {
                elem = args[ i ];
                type = jQuery.type( elem );
                if ( type === "array" ) {
                    // Inspect recursively
                    add( elem );
                } else if ( type === "function" ) {
                    // Add if not in unique mode and callback is not in
                    if ( !flags.unique || !self.has( elem ) ) {
                        list.push( elem );
                    }
                }
            }
        },

3.工具函数fire( context, args )

使用指定的上下文context和参数args调用数组list中的回调函数

jQuery.Callbacks = function( flags ) {
    var // Actual callback list
        list = [],
        // Stack of fire calls for repeatable lists
        stack = [],
        // Last fire value (for non-forgettable lists)
        //memory为undefined表示当前回调函数列表没有被触发过
        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,
        // Fire callbacks
        fire = function( context, args ) {
            args = args || [];
            //如果当前不是memory模式则设置memory为true,
            // 间接地表示当前回调函数列表已经被触发过
            //如果是memory模式,则memory被赋值为[context, args],表示当前回调函数列表被触发过
            memory = !flags.memory || [ context, args ];
            fired = true;
            //变量firing表示回调函数列表是否正在执行
            firing = true;
            firingIndex = firingStart || 0;
            firingStart = 0;
            firingLength = list.length;
            for ( ; list && firingIndex < firingLength; firingIndex++ ) {
                if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
                    memory = true; // Mark as halted
                    break;
                }
            }
            firing = false;
            if ( list ) {
                if ( !flags.once ) {
                    if ( stack && stack.length ) {
                        memory = stack.shift();
                        self.fireWith( memory[ 0 ], memory[ 1 ] );
                    }
                } else if ( memory === true ) {
                    self.disable();
                } else {
                    list = [];
                }
            }
        },
        // Actual Callbacks object
        self = {
            // Have the list do nothing anymore
            disable: function() {
                list = stack = memory = undefined;
                return this;
            },
            // Is it disabled?
            disabled: function() {
                return !list;
            },

4.callbacks.add()

添加回调函数到列表中,通过工具函数add(args)实现。

在memory模式下,并且回调函数列表未在执行中,并且已经被触发过,则立即执行回调函数。

// Add a callback or a collection of callbacks to the list
            add: function() {
                if ( list ) {
                    var length = list.length;
                    add( 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, unless previous
                    // firing was halted (stopOnFalse)
                    } else if ( memory && memory !== true ) {
                        firingStart = length;
                        fire( memory[ 0 ], memory[ 1 ] );
                    }
                }
                return this;
            },

5.callbacks.remove()

从回调函数列表中移除一个或者一组回调函数,移除前修正firingLength和firingIndex

remove: function() {
    if ( list ) {
        var args = arguments,
            argIndex = 0,
            argLength = args.length;
        for ( ; argIndex < argLength ; argIndex++ ) {
            for ( var i = 0; i < list.length; i++ ) {
                if ( args[ argIndex ] === list[ i ] ) {
                    // Handle firingIndex and firingLength
                    if ( firing ) {
                        if ( i <= firingLength ) {
                            firingLength--;
                            if ( i <= firingIndex ) {
                                firingIndex--;
                            }
                        }
                    }
                    // Remove the element
                    list.splice( i--, 1 );
                    // If we have some unicity property then
                    // we only need to do this once
                    if ( flags.unique ) {
                        break;
                    }
                }
            }
        }
    }
    return this;
},

6. callbacks.disable() 和 callbacks.disabled()

1)callbacks.disable():禁用回调函数列表

2)callbacks.disabled():判断函数列表是否被禁用

// Have the list do nothing anymore
            disable: function() {
                list = stack = memory = undefined;
                return this;
            },
 // Is it disabled?
            disabled: function() {
                return !list;
            },

禁用后无法添加、移除、触发回调函数,并会立即停止正在执行的回调函数。

7.锁定 callbacks.lock()、 callbacks.locked()

// Lock the list in its current state
lock: function() {
    //这会导致无法再次触发回调函数
    stack = undefined;
    if ( !memory || memory === true ) {
        self.disable();
    }
    return this;
},
// Is it locked?
locked: function() {
    return !stack;
},

二)jQuery.Deferred(func)

该方法返回一个链式工具对象,我们把该链式工具对象称为"异步队列"。

异步队列的三种状态:

1)待定(pending):初始时处于的状态

2)成功(resolved):调用方法deferred.resolved(args)或resolvedWith(context, args)

将改变异步队列为成功状态,并且立即执行添加的所有"成功回调函数"。

3)失败(rejected):调用方法deferred.reject(args)和方法deferred.rejectWith(context, args)

则改变状态为失败状态,并立即执行所有"失败回调函数"。

一旦异步队列进入成功或者失败状态,就会保持它的状态不变。再次调用以上方法就会被忽略。

异步队列内部维护了三个函数列表:成功回调函数列表、失败回调函数列表、消息回调函数列表。

//可选参数func
Deferred: function( func ) {
    var doneList = jQuery.Callbacks( "once memory" ),
        failList = jQuery.Callbacks( "once memory" ),
        progressList = jQuery.Callbacks( "memory" ),
        //设置初始状态
        state = "pending",
        lists = {
            resolve: doneList,
            reject: failList,
            notify: progressList
        },
        //创建异步队列的只读副本
        promise = {
            done: doneList.add,
            fail: failList.add,
            progress: progressList.add,

            state: function() {
                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() {
                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 ) {
                if ( obj == null ) {
                    obj = promise;
                } else {
                    for ( var key in promise ) {
                        obj[ key ] = promise[ key ];
                    }
                }
                return obj;
            }
        },
        deferred = promise.promise({}),
        key;

    for ( key in lists ) {
        deferred[ key ] = lists[ key ].fire;
        deferred[ key + "With" ] = lists[ key ].fireWith;
    }

    // Handle state
    deferred.done( function() {
        state = "resolved";
    }, failList.disable, progressList.lock ).fail( function() {
        state = "rejected";
    }, doneList.disable, progressList.lock );

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

    // All done!
    return deferred;
},

三)jQuery.when(deferreds)

该方法提供了基于一个或者多个对象的状态来执行回调函数的功能,

通常是基于具有异步事件的异步队列。

猜你喜欢

转载自www.cnblogs.com/Shadowplay/p/9831097.html