zoukankan      html  css  js  c++  java
  • 深入分析,理解jQuery.Deferred源码

    前言: 如果你对jQuery.Callback回调对象不了解,或者只掌握其方法,但是没有通过阅读源码理解,可以先阅读

    前一章jQuery.Callbacks源码解读二,因为只有完全理解jQuery.Callback源码,才能真正的理解并掌握jQuery.Deferred递延对象。

    源码:

    // jQuery 1.10.2
    jQuery.extend({
    
        Deferred: function( func ) {
            /*
                tuples包含三个数组
                三个数组前三项分别代表jQuery回调对象中的 fire, add, jQuery.Callbacks( flag ), 后面扩展的
                deferred[ resolve | resolveWith | done ],promise[ done ]为一组,
                deferred[ reject | rejectWith | fail ],promise[ fail ]为一组,
                deferred[ notify | notifyWith | progress ]为一组,分别为三个不同的回调对象提供操作其内部的接口。
                
                首次看可以忽略下面注释,then中的代码放到最后结合下面的注释理解。
    
                注意: deferred与promise为同一jQuery.Deferred作用域中的,其中在then方法中,又生成了另外一个域中的deferred与promise
                例如: var df = $.Deferred(); var df2 = df.then(args); 其中df2(一个新的promise)与newDefer(一个新的deferred)对应于同一域,理解了
                这一点,才能更好的理解then中的代码。
            */
            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") ]
                ],
    
                state = "pending",
                promise = {
                    state: function() {
                        return state;
                    },
                    always: function() {
                        deferred.done( arguments ).fail( arguments );
                        return this;
                    },
                    then: function( /* fnDone, fnFail, fnProgress */ ) {
                        var fns = arguments;
                        return jQuery.Deferred(function( newDefer ) {
                            // this === deferred; // true;
                            jQuery.each( tuples, function( i, tuple ) {
                                var action = tuple[ 0 ],
                                    fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
                                // deferred[ done | fail | progress ] for forwarding actions to newDefer
                                deferred[ tuple[1] ](function() {
                                    // 说明递延对象状态被改变时,fn才会被调用
                                    var returned = fn && fn.apply( this, arguments );
                                    /*
                                        满足该条件的有以下几种情况:
                                        1、fn为then; 2、fn为Deferred; 1,2排除
                                        3、在fn中显示返回递延对象, 则returned === deferred
                                        在jQuery.Callbacks源码中我们知道 正在执行的回调执行了add操作,则更新firingLength,
                                        因此newDefer[ resolve | reject | notify ]也将执行, 类似fn.apply( this, arguments )
                                    */
                                    if ( returned && jQuery.isFunction( returned.promise ) ) {
                                        returned.promise()
                                            .done( newDefer.resolve )
                                            .fail( newDefer.reject )
                                            .progress( newDefer.notify );
                                    }
                                    /*
                                        与deferred[ resolve | reject | notify ]注释同理
                                        如果deferred[ resolve | reject | notify ]没有被借用,默认第一个参数为promise(即this),这里传递新的newDefer.promise
                                        如果then中传递了函数,则第二个参数为一个数组(元素为该函数的返回值),反之则使用deferred[ resolve | resolveWith ]等传递的参数
                                    */
                                    else {
                                        // 为什么这样写[ returned ],原因是它作为apply的第二个参数
                                        newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
                                    }
                                });
                            });
                            fns = null;
                        }).promise();
                    },
                    // Get a promise for this deferred
                    // If obj is provided, the promise aspect is added to the object
                    promise: function( obj ) {
                        return obj != null ? jQuery.extend( obj, promise ) : promise;
                    }
                },
                deferred = {};
    
            // Keep pipe for back-compat
            // 兼容之前版本,保留pipe方法
            promise.pipe = promise.then;
    
            // Add list-specific methods
            jQuery.each( tuples, function( i, tuple ) {
                var list = tuple[ 2 ],
                    stateString = tuple[ 3 ];
    
                // promise[ done | fail | progress ] = list.add
                promise[ tuple[1] ] = list.add;
    
                // Handle state
                // 如果tuples中一个数组的回调对象被触发时,则改变状态,第三个数组中的回调对象被锁定,另一个被禁用
                // 因此,如果state = 'resolved',执行reject,将不会触发fail执行时添加的回调,因为对应的回调对象被禁用
                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[ resolve | reject | notify ]方法没有被借用( 即deferred.resolve.call(指定对象, args); ),
                        默认第一个参数为 已经扩展的promise对象,否则为借用方法时指定的对象
                    */
                    deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
                    return this;
                };
                deferred[ tuple[0] + "With" ] = list.fireWith;
            });
    
            // Make the deferred a promise
            // 扩展deferred,使其拥有promise中的所有方法
            promise.promise( deferred );
    
            // Call given func if any
            // 将在deferred.then中执行
            if ( func ) {
                func.call( deferred, deferred );
            }
    
            // All done!
            return deferred;
        }
    });

    完整实例1:

    var fn1 = function(){
            console.log('resolved');
        },
        fn2 = function(){
            console.log('rejected');
        },
        fn3 = function(){
            console.log('pending');
        };
    
    var defer = $.Deferred();

    第一种用法:

    1 defer.done(fn1);
    2 defer.resolve(); // resolved

    如果再调用resolve,fn1将不会再执行,因为jQuery.Callbacks('once memory')回调对象中传入标志once

    1 defer.resolve(); // fn1不再执行

    第二种写法:

    1 defer.resolve();
    2 // defer.done(fn1).done(fn1).done(fn1);
    3 defer.done(fn1);  // resolved
    4 defer.done(fn1);  // resolved
    5 defer.done(fn1);  // resolved

    上面写法是因为对应的jQuery.Callbacks('once memory')回调对象中传入了标志memory,回调才会触发;

    以下操作不起作用,原因是fail,reject操作对应的回调对象被禁用

    1 defer.fail(fn2);
    2 defer.reject();

    以下操作也不起作用,原因是progress,notify操作对应的回调对象被锁定;
    这里有一个疑问,为什么在源码中第三个回调对象只是锁定呢,而不是禁用?因为它传入的flag中没有memory,执行锁定实际是禁用

    1 defer.progress(fn3);
    2 defer.notify();

    完整实例2:

    var fn1 = function(str){
            console.log(str + ':' + 'resolved');
        },
        fn2 = function(str){
            console.log(str + ':' + 'rejected');
        },
        fn3 = function(str){
            console.log(str + ':' + 'pending');
        },
        fn4 = function(str){
            console.log(str + ':' + 'resolved' + ' ' + 'again');
        },
        defer, filterd, filterd2;
    
    defer = $.Deferred();
    defer.resolve('state');
    filterd = defer.then(fn1); // state:resolved
    filterd.done(fn4);  // undefined:resolved again

    fn4为什么会执行,因为在then中已经执行了newDefer.resolveWith,而filterd为一个newDefer.promise对象,且对应的jQuery.Callbacks('once memory')回调对象中传入了标志memory;再次,结果第一个子串为什么是undefined,因为then中已经传递了函数,而该函数没有显示返回值,默认返回undefined;

    反之,如果then中没有传递参数,那么回调就可以获取resolve传递过来的参数

    1 filterd2 = defer.then();
    2 filterd2.done(fn4); // state:resolved again

    完整实例3:

    var fn1 = function(str){
            console.log(str + ':' + 'resolved');
            // 在回调中显示返回一个递延对象
            return defer;
        },
        fn4 = function(str){
            console.log(str + ':' + 'resolved' + ' ' + 'again');
        },
        defer, filterd;
    
    defer = $.Deferred();
    defer.resolve('state');
    filterd = defer.then(fn1); // state:resolved
    filterd = defer.done(fn4); // state:resolved again

    转载请注明出处:博客园华子yjh

  • 相关阅读:
    如何将网格式报表打印成其它样式
    拥有与实力不相称的脾气是种灾难——北漂18年(23)
    8.8.1 Optimizing Queries with EXPLAIN
    mysql 没有rowid 怎么实现根据rowid回表呢?
    secondary index
    8.5.5 Bulk Data Loading for InnoDB Tables 批量数据加载
    mysql 中key 指的是索引
    8.5.4 Optimizing InnoDB Redo Logging 优化InnoDB Redo 日志
    8.5.3 Optimizing InnoDB Read-Only Transactions 优化InnoDB 只读事务
    8.5.1 Optimizing Storage Layout for InnoDB Tables InnoDB表的存储布局优化
  • 原文地址:https://www.cnblogs.com/yangjunhua/p/3396824.html
Copyright © 2011-2022 走看看