• jQuery Deferred


    Deferred是jQuery中对CommonJS的异步模型实现,旨在提供通用的接口,简化异步编程难度。

    其是一个可链式操作的对象,提供多个回调函数的注册,以及回调列队的回调,并转达任何异步操作成功或失败的消息。

    由于其对jQuery Callbacks的依赖性,如果没有概念的朋友可以查看jQuery Callbacks

    jQuery.Deferred( [beforeStart ] )

    创建一个Deferred对象。

    beforeStart:

    类型: FunctionDeferred deferred )
    一个在构造函数返回前运行的处理函数。

    resolve、reject、notify

    Defferred中定义了三种动作,resolve(解决)、reject(拒绝)、notify(通知),对应Callbacks对象的fire动作。

    进而又提供了可以定义运行时的this对象的fire,即fireWith,所以又有扩展了三个对应的操作resolveWith、rejectWith、notifyWith。

    内部对应的事件分别是:done(操作完成)、fail(操作失败)、progress(操作进行中),也就是Callbacks对象的add方法添加监听。

    举个简单的例子,我们可以通过deferred.done注册上一个动作完成后的,那么当有地方触发了deferred.resolve或者deferred.resolveWith(这两个方法的差别在于能不能定义回调函数的this对象)时,则回调注册的函数。

    其他对应的也是一样的。

    代码上大概是这样的:

    var dtd = $.Deferred(); // 新建一个deferred对象
    var wait = function(dtd){
        var tasks = function(){
            alert("执行完毕!");
            dtd.resolve(); // 改变deferred对象的执行状态
            };
        setTimeout(tasks,5000);
        return dtd;
    };

    这样我们就有了一个5000ms延迟的wait函数。于是我们就可以这么调用:

    wait(dtd).done(function(){ alert("成功了!"); })
                   .fail(function(){ alert("出错啦!"); });

    then

    then方法提供了三种事件的注册,只要按顺序作为参数传进去就可以了。

    then: function( /* fnDone, fnFail, fnProgress */ ) {
        //分别对应完成后运行的函数,失败后运行的函数,正在运行过程中运行的函数
        var fns = arguments;
        //返回一个新的Deferred的promise,then是上一个Deferred运行后才运行的
        return jQuery.Deferred(function( newDefer ) {
            //分别对不同状态注册函数
            jQuery.each( tuples, function( i, tuple ) {
                var action = tuple[ 0 ],    //取出动作名
                fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];    //取出对应回调函数
                // 分别对当前的Deferred对象注册回调函数,也就是注册deferred[ done | fail | progress ]
                deferred[ tuple[1] ](function() {
                    var returned = fn && fn.apply( this, arguments );
                    //如果传进来的回调函数会返回Deferred对象则在该对象上注册事件
                    if ( returned && jQuery.isFunction( returned.promise ) ) {
                        returned.promise()
                            .done( newDefer.resolve )
                            .fail( newDefer.reject )
                            .progress( newDefer.notify );
                    //否则对创建出来的newDefer执行对应事件
                    } else {
                        //如果上一个函数有返回值则接受传返回值,否则传上一个Deferred传来的参数
                        newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
                    }
                });
            });
            fns = null;
        }).promise();
    },

    Promise

    Promise只提供Deferred对象中的thendonefailalwayspipeisResolved, 和isRejected,防止用户自行改变Deferred的状态。

    完整的Deferred

    jQuery.Deferred = function( func ) {
        var tuples = [
                // 动作, 监听事件, 回调函数列队, 最终状态
                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
                [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
                [ "notify", "progress", jQuery.Callbacks("memory") ]
            ],
            state = "pending",
            //定义promise对象
            promise = {
                //返回当前状态
                state: function() {
                    return state;
                },
                //无论成功还是失败都运行回调函数
                always: function() {
                    deferred.done( arguments ).fail( arguments );
                    return this;
                },
                then: function( /* fnDone, fnFail, fnProgress */ ) {
                //分别对应完成后运行的函数,失败后运行的函数,正在运行过程中运行的函数
                    var fns = arguments;
                    //返回一个新的Deferred的promise,then是上一个Deferred运行后才运行的
                    return jQuery.Deferred(function( newDefer ) {
                        //分别对不同状态注册函数
                        jQuery.each( tuples, function( i, tuple ) {
                            var action = tuple[ 0 ],    //取出动作名
                                fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];    //取出对应回调函数
                            // 分别对当前的Deferred对象注册回调函数,也就是注册deferred[ done | fail | progress ]
                            deferred[ tuple[1] ](function() {
                                var returned = fn && fn.apply( this, arguments );
                                //如果传进来的回调函数会返回Deferred对象则在该对象上注册事件
                                if ( returned && jQuery.isFunction( returned.promise ) ) {
                                    returned.promise()
                                        .done( newDefer.resolve )
                                        .fail( newDefer.reject )
                                        .progress( newDefer.notify );
                                //否则对创建出来的newDefer执行对应事件
                                } else {
                                    //如果上一个函数有返回值则接受传返回值,否则传上一个Deferred传来的参数
                                    newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
                                }
                            });
                        });
                        fns = null;
                    }).promise();
                },
                // 如果deferred存在,将promise合并到deferred里,否则返回prmoise
                promise: function( obj ) {
                    return obj != null ? jQuery.extend( obj, promise ) : promise;
                }
            },
            deferred = {};
        // 向后兼容
        promise.pipe = promise.then;
    
        // 对deferred添加剩余的方法
        jQuery.each( tuples, function( i, tuple ) {
            //取出对应列队
            var list = tuple[ 2 ],
                //取出对应状态
                stateString = tuple[ 3 ];
            // 赋予promise[ done | fail | progress ] = list.add
            promise[ tuple[1] ] = list.add;
            // 对状态添加事件处理
            if ( stateString ) {
                list.add(function() {
                    // 状态state = [ resolved | rejected ]
                    state = stateString;
                // 禁用对各列队[ reject_list | resolve_list ].disable; progress_list.lock
                }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
            }
    
            // 分别注册方法deferred[ resolve | reject | notify ]
            deferred[ tuple[0] ] = function() {
                deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
                return this;
            };
            // 注册有with的方法
            deferred[ tuple[0] + "With" ] = list.fireWith;
        });
    
        // 将promise中的方法合并到deferred里
        promise.promise( deferred );
    
        // 如果jQuery.Deferred中的参数存在,则先用这个参数对deferred改造
        if ( func ) {
            func.call( deferred, deferred );
        }
    
        // 完成
        return deferred;
    };

    jQuery.when

    jQuery.when是一个帮助Deferred队列处理的工具,如果传单一Deferred进去,则会返回其promise,如果传多个Deferred进去,则会新建一个Deferred用以管理该Deferred队列。

    • 如果队列中有一个Deferred失败,则整个队列失败。
    • 如果队列中所有Deferred成功,则整个队列成功。
    • 如果队列中所有Deferred开始运行,则整个队列正在运行。 
    jQuery.when = function( subordinate /* , ..., subordinateN */ ) {
        var i = 0,
            //将arguments转成数组
            resolveValues = core_slice.call( arguments ),
            //传入Deferred对象总数
            length = resolveValues.length,
    
            // 未完成的Deferred总数
            remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
    
            // Deferred队列管理器,如果参数只有一个Deferred则返回该Deferred
            deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
    
            // 更新resolve和progress的Deferred数量,全部处在这两个状态则通知管理器
            updateFunc = function( i, contexts, values ) {
                return function( value ) {
                    contexts[ i ] = this;
                    values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
                    if( values === progressValues ) {
                        deferred.notifyWith( contexts, values );
                    } else if ( !( --remaining ) ) {
                        deferred.resolveWith( contexts, values );
                    }
                };
            },
    
            progressValues, progressContexts, resolveContexts;
    
        // 如果传入Deferred总量大于1,则添加事件处理
        if ( length > 1 ) {
            progressValues = new Array( length );
            progressContexts = new Array( length );
            resolveContexts = new Array( length );
            for ( ; i < length; i++ ) {
                //判断参数是不是可用的Deferred
                if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
                    resolveValues[ i ].promise()
                        //单个成功则更新成功数
                        .done( updateFunc( i, resolveContexts, resolveValues ) )
                        //单个失败则整个列队失败
                        .fail( deferred.reject )
                        //单个开始运行则更新运行中的个数
                        .progress( updateFunc( i, progressContexts, progressValues ) );
                //不可用则未完成数减1
                } else {
                    --remaining;
                }
            }
        }
    
        // 如果没有任何可用Deferred则直接通知管理器,列队完成
        if ( !remaining ) {
            deferred.resolveWith( resolveContexts, resolveValues );
        }
    
        //返回Promise
        return deferred.promise();
    };

    其主要通过内置一个Deferred来管理队列的运行状态,不过其只将Promise暴露在外,而用闭包将所有Deferred保护起来。 

  • 相关阅读:
    python
    【转载】【CPU】关于x86、x86_64/x64、amd64和arm64/aarch64
    【百思不得其解1】诡异的速度差异
    LeakCanary 与 鹅场Matrix ResourceCanary对比分析
    android 插件化框架VitualAPK
    android高级篇收录
    滴滴Booster移动APP质量优化框架 学习之旅 三
    滴滴Booster移动APP质量优化框架 学习之旅 二
    滴滴Booster移动APP质量优化框架 学习之旅
    不一样视角的Glide剖析
  • 原文地址:https://www.cnblogs.com/justany/p/2867444.html
Copyright © 2020-2023  润新知