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)  //没有fire的所以不会输出,如果$.Callbacks('memory')就会直接输出了,可以看看callbacks用法
})

 可以看出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是callbacks函数的主要逻辑方法,可以看看下图 fire(); } }
return this; }, // Call all the callbacks with the given arguments
       //两个方法其实就是一样的,只不过fireWith更具体,部分逻辑在这里
fire: function() { self.fireWith( this, arguments ); return this; },
fire = function() {

            // Enforce single-firing
            locked = options.once;
            fired = firing = true;for ( ; queue.length; firingIndex = -1 ) {
                memory = queue.shift();
                while ( ++firingIndex < list.length ) {
                    if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
                        options.stopOnFalse ) {
                        console.log(options.stopOnFalse)
                        // Jump to end and forget the data so .add doesn't re-fire
                        firingIndex = list.length;
                        memory = false;
                    }
                }
            }

            // Forget the data if we're done with it
            if ( !options.memory ) {
                memory = false;
            }

            firing = false;
            if ( locked ) {
                if ( memory ) {
                    list = [];
                } else {
                    list = "";
                }
            }
        }, 

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

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

  callbacks.add(function() {
    console.log('第一个函数');
  })

  callbacks.add(function() {
    console.log('第二个函数');
  })

  callbacks.fire(); //输出 '第一个函数','第二个函数'
  callbacks.fire(); //未执行
 

当使用memory时在fire调用后使用add,回调函数就会直接被调用,而用once参数时在fire就会只输出一次(实质就是once的参数会告诉callbacks每次fire后都将把函数数列清除),,而callbacks还有'unique','stopOnFalse'的这一些可以添加的参数,具体可以去看一下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来自于上面fireWidth传入的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,progress方法就是类似callbacks add方法,可以看最之前的例子

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

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

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

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

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

然后还可以知道的是,deferred的方法 

done,fail,progress方法统统是用来加入函数数列的方法,他们不会一下子执行,而是在被通知方法通知后才会执行,并且它们的参数来自于

通知方法的参数,以下是状态通知方法

resolve,resolveWith 表示成功,reject,rejectWith表示失败,notify,notifyWith表示过程,

有点乱,那么举个例子,小明去面试后他并不能马上知道自己通过了没,他就会等面试官来告诉他,面试官一直没有回复,那么就相当于面试官传递出来的信息是notify,还在过程中,

那么小明的状态就是等,小明很害羞,不会自己去问,而面试官告诉他,你通过了后,面试官就发送了信息resolve,小明就会做他之前想好的事,果不其然,几天后就来上班了,也就是小明.done(function(){小明上班或者别的}),而面试官说不好意思,你不符合我们的条件,你不用来了,面试官就发送了信息reject,表示拒绝了,那么小明就是,小明.fail(function(){暂时失业}),从中可以看出,面试官才不管小明你要不要来上班,反正他就只是通知你,你过没有过,所以通过后小明所做的事却不是由面试官能决定的,小明完全遵照自己的意愿办事

  • 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 = 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 );
                                    }
                                },

 

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

转载于:https://www.cnblogs.com/Jf-S/p/6340403.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值