首页 > 代码库 > jq Deferred的源码实现

jq Deferred的源码实现

Deferred的实现主要是依靠jq的callbaks方法的,他是对callbacks的封装,先来看看callbacks的一段小代码

var cb = $.Callbacks();
cb.add(function(a){
    console.log(a)
});
cb.fire(‘hello world‘)// 输出a值,hello world;
cb.add(function(a){
  console.log(a)
})

 可以看出cb在调用fire方法时就会执行回调函数,那么他是怎么实现的呢,其实是这样的,callbacks里面有一个数组list用来存储回调函数,而Callbacks通过闭包来返回一个包装了add,fire这一些方法的对象,并且由于闭包的关系数组list不会被处理,那么add方法就是向list里推入回调函数的,最后fire时只要顺序执行下list里的函数就好了,有兴趣的可以去看一下jq的源码,总之,有4点需要得出

  • Point

1 ,这个函数队列里的每一个函数都是靠对象冒充实现的,

2 ,callback.fire() 和 callback.fireWidth()都是可以实现便厉队列的

3 ,callback.fire()的this的来自上下文(表达有些问题,在这是为了区分和fireWith的区别)

4 ,callback.firWith的第一个参数是可以自己定义的,不一定是来自于上下文

看看源码区别下

    fireWith: function( context, args ) {
                if ( !locked ) {
                    args = args || [];
                    args = [ context, args.slice ? args.slice() : args ];
                    queue.push( args );
                    if ( !firing ) {
                        fire();
                    }
                }
                return this;
            },

            // Call all the callbacks with the given arguments
            fire: function() {
                self.fireWith( this, arguments );
                return this;
            },

 

,,,,然后看一下jq callbacks的几个用法

var cb = $.Callbacks(‘memory‘);
cb.add(function(a){
    console.log(a,‘第一次‘)
});
cb.fire(‘hello world‘);
cb.add(function(a){
    console.log(a,‘第二次‘)
});

当使用memory时在fire调用后使用add,回调函数就会直接被调用,而callbacks还有‘once‘,‘locked‘的用法,具体可以去看一下callbacks,不具体说callbacks,,事实上看明白可callbacks就大概可以理解deferred是怎么回事了,假如我是这么写

  • Point
function easyDef(){
    var Def = $.Callbacks(‘memory once‘),
     def = {
    done:Def.add,
    fire:Def.fire,
    fireWith:Def.fireWith
}
     return def;
}
function defTime(arg){
    var def = easyDef(),that = this;
    setTimeout(function(){
        //由于内部时使用apply调用的,所以必须用 []包起来
        def.fireWith(this,[‘接收到远方的消息:你好!!!‘])
    },3000);
    return def;
}
defTime(‘向远方发送消息‘).done(function(data){
    console.log(data,this)
    //接收到远方的消息:你好!!! Window → http://127.0.0.1:8020/newChat/test/deferr.html
    //可以看到this来自于window
})

可以看出一个简单的deferred就实现了,当然真实的不会这么简单,还要考虑很多,下面看看deferred,,,

Deferred拥有三种状况,也就是进程中,成功时,失败后,jq先用数组存储了需要使用的三种状态技术分享

        jQuery.each( tuples, function( i, tuple ) {
            var list = tuple[ 2 ],
                stateString = tuple[ 5 ];
            promise[ tuple[ 1 ] ] = list.add;
            if ( stateString ) {
                list.add(
                    function() {
                        state = stateString;
                    },
                    tuples[ 3 - i ][ 2 ].disable,
                    tuples[ 0 ][ 2 ].lock
                );
            }
            list.add( tuple[ 3 ].fire );
            deferred[ tuple[ 0 ] ] = function() {
                deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
                return this;
            };
            deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
        } );

使用jq的each方法,来重写封装callbacks的方法,最后的得到内部的deferred对象,(在这里需要说一下通过$.Deferred()产生的def对象启示是由两个对象组成的,之后通过promise的promise对象方法将内部deferred对象和promise对象组合成了一个终极deferred对象,为了区分,这个deferred对象就叫做内部deferred对象好了,看不明白看下面的图)可以看一下dererred对象到底是怎样的,首先看一下所谓的两个对象

//Deferred的promise对象和deferred对象,使用promise对象的promise方法扩展deferred对象方法,其实就是jq的extends方法
//扩展deferred,返回最终的deferred对象 promise.promise( deferred );
  promise = {
                state: function() {
                    return state;
                },
                always: function() {
                    deferred.done( arguments ).fail( arguments );
                    return this;
                },
                "catch": function( fn ) {
                    return promise.then( null, fn );
                },

                // Keep pipe for back-compat
                pipe: function( /* fnDone, fnFail, fnProgress */ ) {
              /*  */
                },
                then: function( onFulfilled, onRejected, onProgress ) {
                    /*  */
                },
                promise: function( obj ) {
                    return obj != null ? jQuery.extend( obj, promise ) : promise;
                }
            },
            deferred = {};

技术分享

上图就是返回的deferred最终的对象,拥有许多方法,done,fail,notify方法就是类似callbacks add方法,可以看最之前的例子

接下来看看内部deferred,由于这些方法都类似,所以就拿done来作为例子 ,在之前的

jQuery.each( tuples, function( i, tuple ) {})过后,那么done本身就有了4个回调函数

fn1用于修改状态,表示在这个回调函数执行后的状态

fn2,fn3用于关闭队列和告知函数队列已不在执行

fn4 是添加一个新建callbacks的fire对象,(这点在讲then时很重要,看看之前callbacks的4点,我说过执行函数队列时时对象冒充的,具体下面讲) 

  • Point

var defo = $.Deferred();

defo.done(function(data){

console.log(data)

//Object { a: 1, b: 2, c: 3 }

})

defo.resolveWith(this,[{a:1,b:2,c:3}])

//当执行了resolveWith方法时,就会执行队列里的函数,

//遵照上面写的4个fn,加上上面done的一个,所以一共是5个fn将被执行,

//由于jQuery.Callbacks( "once memory" )是打开了memory和once的,所以执行过后,函数队列将被清除,下一次done会直接执行,

//并且回调的参数将一直沿用resolveWith所传参数,可以联想到 jq ajax的def实现,

defo.done(function(data){

console.log(data)

//Object { a: 1, b: 2, c: 3 }

})

$.ajax({

type:"get",

url:"1.json",

async:true

}).done(function(data){console.log(data)})

.done(function(data){

///$.ajax()返回的即使def对象 第二次使用done将不在去请求,因为之前已经成功过了

console.log(data)

});

大致的deferred的源码实现就是这样了,其他还有他的一些方法,看看then的方法是如何实现的,我感觉是比较绕,,不记下来就有点乱,这才是我真正想记下来的目的,其他容易记,这个太绕了,首先看看then的用法

  • Point

timeDef = function(target) {

var def = $.Deferred()

setTimeout(function() {

def.resolveWith(def, [‘向‘ + target + ‘发出问候,问候被接受‘]);

}, 3000)

//def.promise返回的是promise对象,该对象只有done,fail等加入函数队列类方法

//,没有resolve等执行方法,可以避免外界去执行这个def对象(外界执行def.resolve()就会让def失去意义);

return def.promise();

};

var hi = timeDef(‘/小明‘).

then(function(data) {

console.log(data)

// 向/小明发出问候,问候被接受

return timeDef(‘/小刚‘);

}).

done(function(data) {

//向/小刚发出问候,问候被接受

console.log(data)

})

hi.done(function(data) {

console.log(‘状态:‘ + this.state() + ‘,你已‘ + data + ‘‘)

}).

then(function(data){

console.log(‘状态:‘ + this.state() + ‘,你已‘ + data + ‘‘)

}).

then(function(data){

console.log(data)

//结果为undefined,因为之前没有返回def对象,过程被关闭,

//that = undefined;

//args = [ returned ];

//上面源码的话,意思就是,this = undefined;data = http://www.mamicode.com/undefined

})

 

then就是对def对象的过程式执行,,只要then的回调return def对象时,就会开启准备 执行下一个 def对象 then可以传入三个参数then(fn,fn,fn),

分别是成功,失败,过程中的回调函数,调用then后会 对为之前的def对象(倘若之前也是then并且then的回调返回了def对象,那么就处理这个返回的对象,如果此回调没有返回对象,那么表示def对象过程已经结束了,继续then已经没有用了,参见上面的代码),

下面这是部分的then源码

 jQuery.Deferred( function( newDefer ) {
                        tuples[ 0 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onProgress ) ?
                                    onProgress :
                                    Identity,
                                newDefer.notifyWith
                            )
                        );
                        tuples[ 1 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onFulfilled ) ?
                                    onFulfilled :
                                    Identity
                            )
                        );
                        tuples[ 2 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onRejected ) ?
                                    onRejected :
                                    Thrower
                            )
                        );
                    } ).promise();

 

可以看到 它给tuples的一个callbacks添加了队列函数, 我暂时称它为then_callback,下面也是这样,可以参照上面的图,再来看一下吧

技术分享技术分享

所以添加的是fn4,fire方法,所以当xx.then的xx执行好了后,就会执行xx的fn1,fn2,..函数,而fn4被执行后(也就是fire()被执行)所以就会执行then_callbacks,而then方法被执行后就是在已在then_callbacks推入了队列函数,所以将开始执行队列函数,可见下面,以下是源码,部分已被我部分删掉,resolve执行的回调

                    function resolve( depth, deferred, handler, special ) {
                        return function() {
                            var that = this,
                                args = arguments,
                                mightThrow = function() {
                                    var returned, then;
                                    if ( depth < maxDepth ) {
                                        return;
                                    }
                                    //对象冒充执行 args就是数据
                                    returned = handler.apply( that, args );
                                    //如果有returned 就是说then的回调函数有return def对象
                                    then = returned &&
                                        ( typeof returned === "object" ||
                                            typeof returned === "function" ) &&
                                        returned.then;
                                    //then是否为函数
                                    if ( jQuery.isFunction( then ) ) {
                                        //对progress的处理
                                        if ( special ) {
                                            then.call(
                                                returned,
                                                resolve( maxDepth, deferred, Identity, special ),
                                                resolve( maxDepth, deferred, Thrower, special )
                                            );

                                        } else {
                                            maxDepth++;
                                            //为return的def对象添加了then_callbacks的函数队列
                                            then.call(
                                                returned,
                                                resolve( maxDepth, deferred, Identity, special ),
                                                resolve( maxDepth, deferred, Thrower, special ),
                                                resolve( maxDepth, deferred, Identity,
                                                    deferred.notifyWith )
                                            );
                                        }

                                    // Handle all other returned values
                                    } else {
                      //then没有返回return
                                        if ( handler !== Identity ) {
                                            that = undefined;
                                            args = [ returned ];
                                        }
                                        //当return的def对象执行了后,把参数,上下文传给本then,使其可以继续有过程
                                        //a.then(return def) def执行, a.then返回的一个def被执行,并且参数来自def
                                        ( special || deferred.resolveWith )( that, args );
                                    }
                                },

 

上面大概就是这样,话理的其实不是很清楚,不知道下次来看会不会蒙圈,有问题欢迎指出

jq Deferred的源码实现