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)

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

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

原文地址:https://www.cnblogs.com/Shadowplay/p/9831097.html

时间: 2024-10-15 19:19:10

jquery 1.7.2源码解析(四) 异步队列Deferred Object的相关文章

jQuery 源码解析(八) 异步队列模块 Callbacks 回调函数详解

异步队列用于实现异步任务和回调函数的解耦,为ajax模块.队列模块.ready事件提供基础功能,包含三个部分:Query.Callbacks(flags).jQuery.Deferred(funct)和jQuery.when().本节讲解Callbacks,也就是回调函数列表 回调函数用于管理一组回调函数,支持添加.移除.触发.锁定和禁用回调函数,为jQuery.ajax.jQuery.Deferred()和ready()事件提供基础功能,我们也可以基于它编写新的组件. 使用方法:$.Callb

Spring 源码解析之ViewResolver源码解析(四)

Spring 源码解析之ViewResolver源码解析(四) 1 ViewResolver类功能解析 1.1 ViewResolver Interface to be implemented by objects that can resolve views by name. View state doesn't change during the running of the application, so implementations are free to cache views. I

Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?

Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的? ??如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一样,本篇文章最最核心的要点就是 SqlSession实现数据库操作的源码解析.但按照惯例,我这边依然列出如下的问题: 1. SqlSession 是如何被创建的? 每次的数据库操作都会创建一个新的SqlSession么?(也许有很多同学会说SqlSession是通过 SqlSessionFactor

iOS即时通讯之CocoaAsyncSocket源码解析四

原文 前言: 本文为CocoaAsyncSocket源码系列中第二篇:Read篇,将重点涉及该框架是如何利用缓冲区对数据进行读取.以及各种情况下的数据包处理,其中还包括普通的.和基于TLS的不同读取操作等等.注:由于该框架源码篇幅过大,且有大部分相对抽象的数据操作逻辑,尽管楼主竭力想要简单的去陈述相关内容,但是阅读起来仍会有一定的难度.如果不是诚心想学习IM相关知识,在这里就可以离场了... 注:文中涉及代码比较多,建议大家结合源码一起阅读比较容易能加深理解.这里有楼主标注好注释的源码,有需要的

AFNetworking2.0源码解析&lt;四&gt;

结构 AFURLResponseSerialization负责解析网络返回数据,检查数据是否合法,把NSData数据转成相应的对象,内置的转换器有json,xml,plist,image,用户可以很方便地继承基类AFHTTPResponseSerializer去解析更多的数据格式,AFNetworking这一套响应解析机制结构很简单,主要就是两个方法: 1.-validateResponse:data:error: 基类AFHTTPResponseSerializer的这个方法检测返回的HTTP

vuex 源码解析(四) mutation 详解

mutation是更改Vuex的store中的状态的唯一方法,mutation类似于事件注册,每个mutation都可以带两个参数,如下: state ;当前命名空间对应的state payload   ;传入的参数,一般是一个对象 创建Vuex.Store()仓库实例时可以通过mutations创建每个mutation 我们不能直接调用一个mutation,而是通过 store.commit来调用,commit可以带两个参数,如下: type ;对应的mutation名 payload ;传入

jQuery源码分析(九) 异步队列模块 Deferred 详解

deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些Ajax操作,动画操作等.(P.s:紧跟上一节:https://www.cnblogs.com/greatdesert/p/11433365.html的内容) 异步队列有三种状态:待定(pending).成功(resolved)和失败(rejected),初始时处于pending状态 我们可以使用jQuery.Deferred创建一个异步队列,返回一个对象,该对象含有如下操作: done(fn/arr

Tomcat关闭过程(Tomcat源码解析四)

我们在Tomcat启动过程(Tomcat源代码阅读系列之三)一文中已经知道Tomcat启动以后,会启动6条线程,他们分别如下: "ajp-bio-8009-AsyncTimeout" daemon prio=5 tid=7f8738afe000 nid=0x115ad6000 waiting on condition [115ad5000] "ajp-bio-8009-Acceptor-0" daemon prio=5 tid=7f8738b05800 nid=0x

volley源码解析(四)--CacheDispatcher从缓存中获取数据

从上一篇文章我们已经知道,现在要处理的问题就是CacheDispatcher和NetworkDispatcher怎么分别去缓存和网络获取数据的问题,这两个问题我分开来讲. 但是首先说明的是,这两个问题其实是有联系的,当CacheDispatcher获取不到缓存的时候,会将request放入网络请求队列,从而让NetworkDispatcher去处理它: 而当NetworkDispatcher获得数据以后,又会将数据缓存,下次CacheDispatcher就可以从缓存中获得数据了. 这篇文章,就让