jQuery-v2.0.3源码浅析04-Deferred

接下来我们来看下jQuery的延迟对象Deferred。
我们前面讲过Callbacks函数,其实Deferred就是在此基础进行了扩展,Deferred是对异步函数的统一管理。

源码

/**源码2999行**/
jQuery.extend({
    Deferred: function( func ) {
        var tuples = [
                // 类似Callbacks的fire, 类似Callbacks的add, Callbacks, 执行完毕的状态
                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
                [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
                [ "notify", "progress", jQuery.Callbacks("memory") ]
            ],
            state = "pending",//没有执行之前状态默认为state
            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 ) {
                        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() {
                                var returned = fn && fn.apply( this, arguments );
                                if ( returned && jQuery.isFunction( returned.promise ) ) {
                                    returned.promise()
                                        .done( newDefer.resolve )
                                        .fail( newDefer.reject )
                                        .progress( newDefer.notify );
                                } else {
                                    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
        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
            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;
            };
            deferred[ tuple[0] + "With" ] = list.fireWith;
        });

        // Make the deferred a promise
        promise.promise( deferred );

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

        // All done!
        return deferred;
    }
});

根据代码我们能够解构出该函数主要的两个对象promise 和 deferred

promise解构

promise = {
  state,
  always,
  pipe = then,
  promise,
  done,
  fail,
  progress
}

deferred解构

deferred = {
  state,
  always,
  pipe = then,
  promise,
  done,
  fail,
  progress,
  resolve,
  reject,
  notify,
  resolveWith,
  rejectWith,
  notifyWith
}

相信看到这里很多理解了Callbacks的同学已经发现了,其实deferred 和 promise的区别其实就是,deferred对象提供了Callbacks的"fire"方法,而promise只有Callbacks的"add"。不过肯定很多同学肯定有跟我一样的疑惑,为什么要这样设计呢?其实也是比较好理解的,就拿ajax来说吧,打比方说我们ajax获取数据是否成功的状态能够被我们自己改变吗,答案是不能。所以deferred是提供给"内部方法"使用的,如果给"外部"使用我们就返回promise对象。例如:

function getDef(){
    var def = $.Deferred();
    //模拟延迟操作
    setTimeout(function(){
        def.resolve();
    }, 1000);
    return def.promise();
}
var def = getDef();
def.done(function(){
    console.log(1);
});

这个时候getDef()所返回的promise已经不包含'fire'方法,所以状态不会被随便串改。

假设我们上面getDef函数直接返回def,是不是会存在下面这种情况

function getDef(){
    var def = $.Deferred();
    //模拟延迟操作
    setTimeout(function(){
        def.resolve();
    }, 1000);
    return def;
}
var def = getDef();
def.done(function(){
    console.log(1);
});
def.resolve();
//发现程序一执行控制台就输出1了。

接下来我们来具体看一下源码是怎么实现的吧。

/**源码3046行**/
var tuples = [
  [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
  [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
  [ "notify", "progress", jQuery.Callbacks("memory") ]
]
/**源码3051行**/
jQuery.each( tuples, function( i, tuple ) {
    var list = tuple[ 2 ],
        stateString = tuple[ 3 ];

    // promise[ done | fail | progress ] = list.add
    /**源码3056行**/
    promise[ tuple[1] ] = list.add;

    // Handle state
    /**源码3059行**/
    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 ]
    /**源码3069行**/
    deferred[ tuple[0] ] = function() {
        deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
        return this;
    };
    deferred[ tuple[0] + "With" ] = list.fireWith;
});

首先来看下tuples变量吧
1、resolve、reject、notify 等于 Callbacks 方法中提供的fire方法。源码3069行进行的赋值。
2、done、fail、progress 等于Callbacks 方法中提供的add方法。源码3056行进行的赋值。
3、源码3059行,会发现源码对 state = [ resolved | rejected ] 的list,add了方法,其实这段代码的意思是指 当执行玩resolve和reject,对state的状态进行修改,如果执行完resolve(成功)就不能再执行reject(失败)了。并对进行中的list进行了锁定操作。

接下来我们来看一下稍微复杂点的then方法,首先贴上源码

/**源码3017行**/
then: function( /* fnDone, fnFail, fnProgress */ ) {
    var fns = arguments;
    return jQuery.Deferred(function( newDefer ) {
        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
            /**源码3024行**/
            deferred[ tuple[1] ](function() {
                var returned = fn && fn.apply( this, arguments );
                if ( returned && jQuery.isFunction( returned.promise ) ) {
                    /**源码3071行**/
                    returned.promise()
                        .done( newDefer.resolve )
                        .fail( newDefer.reject )
                        .progress( newDefer.notify );
                } else {
                                        /**源码3076行**/
                    newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
                }
            });
        });
        fns = null;
    }).promise();
}

了解源码之前我们先来看看then有哪些写法,首先是第一种比较简单的写法,可以分别传3个回调函数分别对应3个状态的回调函数

function getDef(){
    var def = $.Deferred();
    //模拟延迟操作
    setTimeout(function(){
        def.resolve();
        //def.reject();
        //def.notify();
    }, 1000);
    return def;
}
var def = getDef();
def.then(function(){
    console.log('成功');
}, function(){
    console.log('失败');
}, function(){
    console.log('进行中');
});

该功能的实现代码是在源码的3024行实现的,将传入的三个函数分别add [done | fail | progress] 到对应的list中,最后直接通过var returned = fn && fn.apply( this, arguments );这句来执行回调函数。

then的第二种使用方法

function getDef(){
    var def = $.Deferred();
    //模拟延迟操作
    setTimeout(function(){
        def.resolve();
        // def.resolve();
        // def.notify();
    }, 1000);
    return def;
}
var def = getDef();
def.then(function(){
    console.log('成功');
    return '123';
}, function(){
    console.log('失败');
}, function(){
    console.log('进行中');
}).then(function(a){
    console.log(a);
}, function(){
    console.log('失败1');
}, function(){
    console.log('进行中1');
});

then每次执行完毕之后都会返回一个promise(包含then方法),所以一直通过then进行链接。然后通过源码3076行进行参数传递。如果return的是一个Deferred对象,则可以通过3071行实现链式写法。例如:

function getDef(){
    var def = $.Deferred();
    //模拟延迟操作
    setTimeout(function(){
        def.resolve();
        // def.resolve();
        // def.notify();
    }, 1000);
    return def.promise();
}
var def = getDef();
def.then(function(){
    console.log('成功');
    var def1 = getDef();
    return def1;
}).done(function(){
    console.log('成功1');
});

程序执行1秒之后打印成功,然后再过一秒打印出成功1

你可能感兴趣的:(jQuery-v2.0.3源码浅析04-Deferred)