zoukankan      html  css  js  c++  java
  • javascript源代码学习之五——jQuery.deferred

    后续ES6的promise就源于此,这部分很重要!

    jQuery.Defered——异步队列用于管理一组回调函数(成功resolve,失败reject,消息progress),基于上一节实现的jQuery.callbacks完成。

    done,fail,progress分别表示添加成功,失败,消息回调函数。和callbacks中的add相对应;resolve/resolveWith,reject/rejectWith,notify/notifyWith分别代表触发成功/失败/消息回调,和callbacks中的fire/fireWith相对应;

    整体的代码结构如下:

    jQuery.extend({
        Deferred:function(func){
    
            var //其他辅助性变量
                //3个回调序列
                doneList=jQuery.Callbacks('once memory'),
                failList=jQuery.Callbacks('once memory'),
                progressList=jQuery.Callbacks('memory'),
                state='pending',
                promise:{
                    state:function(){return state},
                    done:doneList.add,
                    fail:failList.add,
                    progress:progressList.add,
                    promise:function(obj){},
                    pipe:funciton(fnDone,fnfFail,fnProgress){},
                    then:function(fnDone,fnFail,fnProgress){},
                    always:function(fn){},
                },
                deferred:promise.promise({}),
                //为defered添加resolve,resolveWith,reject,rejectWith,progress,progressWith等和fire/fireWith对应的方法
                //....
                //添加成功,失败回调,更改state状态,并设置其他对应的回调函数列表为禁用状态
                //....
                //执行func
                if(func){
                    func.apply(deferred,deferred);
                }
            
        }
    });

     不说了,直接上自己实现的Deferred代码,这部分阅读,撰写代码的心得都包含在注释里了

       jQuery.extend({
            //func参数仅内部使用,func的调用者是jQuery.Deferred的返回值,参数也是
            Deferred:function(func){
                var doneList=jQuery.Callbacks('once memory'),
                    failList=jQuery.Callbacks('once memory'),
                    progressList=jQuery.Callbacks('memory'),
                    state='pending',
                    list={
                        'resolve':doneList,
                        'reject':failList,
                        'notify':progressList
                    },
                    promise={
                        done:doneList.add,
                        fail:failList.add,
                        progress:progressList.add,                    
                        state:function(){
                            return state;
                        },
                        //同时添加成功,失败,消息回调函数
                        then:function(doneCallback,failCallback,progressCallback){
                            deferred.done(doneCallback).fail(failCallback).progress(progressCallback);
                        },
                        //成功,失败时,添加同一个处理函数
                        always:function(){
                            deferred.done(arguments).fail(arguments);
                        },
                        //说实话,能看懂这个源代码,但搞不太懂这个pipe是干嘛用的
                        //实际使用中调用的地方也不多
                        //不过其源代码有不少知识点值得学习
                        pipe:function(fnDone,fnFail,fnProgress){
                            //这里的newDefer,就是调用jQuery.Deferred(function(newDeferred))返回的异步队列对象,由这部分代码最终的func.apply(deferred,deferred)决定;
                            return jQuery.Deferred(function(newDefer){
    
                                jQuery.each({
                                    done:[fnDone,'resolve'],
                                    fail:[fnFail,'reject'],
                                    progress:[fnProgress,'notify']                              
                                },function(handler,data){
                                    //注意这三个局部变量定义的位置,只能定义在该闭包中,如果定义在jQuery.Deferred得到的只是函数最后的值,如果没有传递fnProgress,就会报出undefined的错误
                                    var action=data[1],
                                        fn=data[0],
                                        returned;
                                    if(jQuery.isFunction(fn)){
                                        //通过done,fail,progress添加的方法,只有在对应的回调函数队列fire的时候才会触发
                                        deferred[handler](function(){
                                            //这里的this,arguments是调用fire/fireWith时候传递
                                            //这里的this可以通过fireWith中指定context,arguments也是fire/fireWith的时候传递的参数
                                            
                                            returned=fn.apply(this,arguments);
                                            //如果函数的返回值依旧是一个异步队列,则将jQuery.pipe返回的异步队列的成功,失败,消息回调添加到返回的retuned对应的回调列表中
                                            if(returned&&jQuery.isFunction(returned.promise)){
                                                returned.promise().then(newDefer.resolve,newDefer.reject,newDefer.notify);
                                            }else{
                                                //如果函数返回值不是异步队列,则jQuery.pipe()返回的异步队列对应状态的方法立即触发
                                                newDefer[action+'With'](this===deferred?newDefer:this,[returned]);
                                            }
                                        });
                                    }else{
                                        deferred[handler](newDefer[action]);
                                    }
    
                                });
                            }).promise();
                        },
                        //注意promise()和promise({})这两种写法是完全不同的,前者返回异步对象的只读版本,后者返回一个副本
                        promise:function(obj){
                            return obj==null?promise:jQuery.extend(obj,promise);
                        },  
                    },
                    deferred=promise.promise({}),
                    key;
                    //为deferred添加状态改变的相关函数,与fire,fireWith相对应
                for(key in list){
                    deferred[key]=list[key].fire;
                    deferred[key+'With']=list[key].fireWith;
                }
                deferred.done(function(){
                    state='resolved';
                },failList.disable,progressList.disable)
                .fail(function(){
                    state='rejected';
                },doneList.disable,progressList.disable);
    
                    
                if(func){
                    //这句话决定了,通过jQuery.Deferred(func)调用的时候,func的context和参数
                    func.call(deferred,deferred);
                }
                return deferred;
    
            },
        
       }

     截止目前的myJquey.js

    (function(window,undefined){
        var rootjQuery,
            core_version='2.0.3',
            idExpr=/^#([w-]*)$/,
            //下面两个正则用于转驼峰
            rmsPrefix = /^-ms-/,
            rdashAlpha = /-([da-z])/gi,
            rnotwhite = /S+/g,//匹配非空白字符
    
            class2type={},
            core_deletedIds=[],
            core_version='2.0.3',
    
            _jQuery=window.jQuery,
            _$=window.$,
    
            core_toString=class2type.toString,
            core_hasOwn=class2type.hasOwnProperty,
            core_trim=core_version.trim,
            core_indexOf=core_deletedIds.indexOf,
            core_push=core_deletedIds.push,
            core_concat=core_deletedIds.concat,
            core_slice=core_deletedIds.slice,
    
            //用于jQuery.camelCase转驼峰函数中
            //当replace函数只有一个匹配项时,第二个参数可以是一个函数
            //如果repalce中的正则没有捕获组,会向这个函数传递三个参数:模式的匹配项,模式匹配项在字符串中的位置,原始字符串
            //如果replace中的正则有捕获组,也会向这个函数传递三个参数,模式的匹配项,捕获组的匹配项,模式匹配项在字符串中的位置
            fcamelCase=function(all,letter){
               return letter.toUpperCase();            
            },
            jQuery=function(selector,context){
                return new jQuery.fn.init(selector,context,rootjQuery);
            };
         
        //jQuery相关实例方法和属性
        jQuery.fn=jQuery.prototype={
            jQuery:core_version,//其实就是版本字符串2.0.3
            constructor:jQuery,//还原constructor指向
            selector:'',//含有连续的整型属性、length属性、context属性,selector属性(在jQuery.fn.init中设置),preObject属性(在pushStack中设置)
            length:0,
            init:function(selector,context,rootjQuery){
                var match,elem;
               //selector是选择器表达式
               if(!selector){
                return this;
               }
    
               if(typeof selector ==='string'){
                    match=idExpr.exec(selector);
                    if(match&&!context){
                        elem=document.getElementById(match[1]);
                        if(elem&&elem.parentNode){
                            this[0]=elem;
                            this.length=1;                        
                        }
                        this.selector=selector;
                        this.context=document;
                        return this;
                    }else{
                        //说明是复杂的选择器表达式,这里暂不考虑
                    }                
               }
               //处理selector是DOM元素的情形
               if(selector&&selector.nodeType){
                    this[0]=selector;
                    this.length=1;
                    this.context=selector;
                    return this;
               }
               //处理selector是函数的情形
               if(jQuery.isFunction(selector)){
                    return rootjQuery.ready( selector );
               } 
               //处理selector是jQuery对象的情形
               if(selector.selector){
                    this.selector=selector.selector;
                    this.context=selector.context;
               }
               //处理其他情形
               return jQuery.makeArray(selector,this);
    
            },
            //将jQuery类数组对象转换为数组
            toArray:function(){
                return core_slice.call(this);
            },
            //如果传递了参数num,代表获取下标num的DOM元素(num可以为负数)
            //如果没有传递num,则将jQuery对象转换为数组后整体返回
            get:function(num){
                if(num==null){//注意这里不能用!num,因为num可以为0
                    return this.toArray();
                }
                return num<0?this[num+this.length]:this[num];
            },
            //入栈
            pushStack:function(elems){
    
                var ret=jQuery.merge(this.constructor(),elems);
                
                ret.prevObject=this;
                ret.context=this.context;
                return ret;
            },
            //遍历jQuery对象
            each:function(callback,args){
                //在静态方法已经指定了callback的执行上下文
               return jQuery.each(this,callback,args);
            },
            //加载完成事件方法,这里暂不考虑
            ready:function(fn){},
            slice:function(){      
                //注意apply和call的区别                          
                return this.pushStack(core_slice.apply(this,arguments));
            },
            first:function(){
                return this.get(0);
            },
            last:function(){
                return this.get(-1);
            },
            eq:function(i){
                var length=this.length,
                    j=+i+(i<0?length:0);
                return this.pushStack(j>=0&&j<length?[this[j]]:[]);
            },
            map:function(callback){
                //这种写法不能指定callback的执行环境,因为在静态方法jQuery.map并没有指定callback的执行上下文
                // return this.pushStack(jQuery.map(this,callback));
               return this.pushStack(jQuery.map(this,function(elem,i){                 
                    return callback.call(elem,i,elem);
               }));
            },
            //与pushStack方法相对应,返回栈的上一级
            end:function(){
                return this.prevObject||this.constructor();
            },        
            push:core_push,
            sort:[].sort,
            splice:[].splice,
        };
        jQuery.fn.init.prototype=jQuery.fn; 
    
    
        //可接受的参数类型如下:jQuery.extend([deep],target,object1,[objectN])
        jQuery.extend=jQuery.fn.extend=function(){
            var target=arguments[0]||{},//指向目标对象
                deep=false,//是否进行深度复制
                i=1,//表示源对象的起始下标
                length=arguments.length,//表示参数个数;
                options,name,src,copy,copyIsArray;//options指向某个源对象,name指向源对象的某个属性名,src目标对象某个属性的原始值,copy某个源对象的某个属性的值,copyIsArray指示变量copy是否为数组        
            //首先进行参数修正
            if(typeof target==='boolean'){
                deep=target;
                target=arguments[1]||{};
                i=2;
            }
            //此时target就是jQuery或jQuery.fn
            if(i===length){
                target=this;
                i--;
            }
            //处理target是字符串或者其他情形,这在深度复制中可能出现
            // if(typeof target!=='object'||!jQuery.isFunction(target)){
            //     target={};
            // }
            for(i;i<length;i++){
                options=arguments[i];
                for(name in options){
                    src=target[name];
                    copy=options[name];
                    if(deep&&copy&&(jQuery.isPlainObject(object)||(copyIsArray=jQuery.isArray(object)))){
                        if(copyIsArray){
                            copyIsArray=false;
                            clone=src&&jQuery.isArray(src)?src:[];
                        }else{
                            clone=src&&jQuery.isPlainObject(src)?src:{};
                        }
                        target[name]=jQuery.extend(deep,clone,copy);
                    }else{
                        target[name]=copy;
                    }
                }    
            }
            return target;
        };
        //检查是否是数组或者类数组
        function isArrayLike(obj){
            var length=obj.length,
                type=jQuery.type(obj);
            if(obj&&jQuery.isWindow(obj)){
                return false;
            }
            if(obj.nodeType===1&&length){
                return true;
            }        
    
            if(type==='array'){
                return true;
            }
            if(typeof length==='number'&&(length==0||(length>0&&(length-1) in obj))){
                return true;
            }        
            return false;
        }
        jQuery.extend({
            //一堆静态方法和属性
            expando:'jQuery'+(core_version+Math.random()).replace(/D/g,''),
            // 该函数用于释放jQuery对于全局变量$的控制权,可选的参数deep代表是否释放对全局变量jQuery的控制权
            noConflict:function(deep){
                if(window.$===jQuery){
                    window.$=_$;
                }
                if(deep&&window.jQuery===jQuery){
                    window.jQuery=_jQuery;
                }
                return jQuery;
            },
            /********isReady,readyWait,holdReay,ready与加载事件有关,暂且略过***********/
            isReady:false,
            readyWait:1,
            holdReady:function(hold){},
            ready:function(){},
            /*******/
    
    
            /****下面是一系列类型检测的静态方法*******/
            isFunction:function(obj){
                //如果使用typeof,在有些浏览器中,正则也会返回function,因此这里采用jQuery处理后的方法,jQuery.type
                return jQuery.type(obj)==='function';
            },
            isArray:Array.isArray,
            isWindow:function(obj){
                return obj!==null&&obj===obj.window;
            },
            //判断obj是否为数字或者数字类型的字符串,并且是有效数字
            isNumeric:function(obj){
                return !isNaN(parseFloat(obj))&&isFinite(obj);
            },
            type:function(obj){
                if(obj===null){
                    return String(null);
                }
                //Date,Array等类型typeof都会返回object,function、正则(部分浏览器)中 typeof都会返回function
                 
                if(typeof obj==='object'||typeof obj==='function'){                
                    return class2type[core_toString.call(obj)]||'object';
                }
                return typeof obj;
            },
            //判断是否为以下两种情况:1,对象字面量;2,通过new Object()创建
            isPlainObject:function(obj){
                if(jQuery.type(obj)!=='object'||obj.nodeType||jQuery.isWindow(obj)){
                    return false;
                }
    
                //如果是纯粹的对象,那么obj一定有constructor属性,并且方法hasOwnPropertyOf一定就在构造函数本身的原型中,而不用通过原型链查找得到
               if(obj.constructor&&!core_hasOwn.call(obj.constructor.prototype,'isPrototypeOf')){
                    return false;
               }
               return true;
    
            },
            //检查是否是空对象
            isEmptyObject:function(obj){
                for(var name in obj){
                    return false;
                }
                return true;
            },
            /******类型检测静态方法结束********/
    
            error:function(msg){
                throw new Error(msg);
            },
            //将html字符串转换为html DOM结构,
            parseHTML: function( data, context, keepScripts ){
    
            },
            parseJSON:JSON.parse,
            parseXML:function(data){
                var xml, tmp;
                if ( !data || typeof data !== "string" ) {
                    return null;
                }
    
                // Support: IE9
                try {
                    tmp = new DOMParser();
                    xml = tmp.parseFromString( data , "text/xml" );
                } catch ( e ) {
                    xml = undefined;
                }
    
                if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
                    jQuery.error( "Invalid XML: " + data );
                }
                return xml;
            },
            noop:function(){},
            //用于在全局作用域执行javascript代码,这里暂略
            globalEval:function(data){},
            //转换连字符字符串为驼峰类型
            camelCase:function(string){
                return string.replace(rmsPrefix,'ms-').replace(rdashAlpha,fcamelCase);
            },
            //判断elem的nodeName是否=name
            nodeName:function(elem,name){
                return elem.nodeName&&elem.nodeName.toLowerCase()==name.toLowerCase();
            },
            //jQuery遍历方法,其中args是传递给回调callback的参数,仅供jQuery内部使用;外部调用该方法时,回调的参数默认为数组下标/对象key,对应数组值/对象value
            each:function(object,callback,args){
                var i,
                    value, 
                    length=object.length,
                    isArray=isArrayLike(object);
    
                if(args){//说明是内部调用
                    if(isArray){
                        for(i=0;i<length;i++){
                           value= callback.call(object[i],args);
                           if(value===false){
                                break;
                           }
                        }
                    }else{
                        for(i in object){
                            value=callback.call(object[i],args);
                            if(value===false){
                                break;
                            }
                        }
                    }
                }else{
                    if(isArray){
                        for(i=0;i<length;i++){
                            value=callback.call(object[i],i,object[i]);
                            if(value===false){
                                break;
                            }
                        }
                    }else{
                        for(i in object){
                            value=callback.call(object[i],i,object[i]);
                            if(value===false){
                                break;
                            }
                        }
                    }
                }
                return object;
            },
            trim:function(str){
                return str==null?'':core_trim.call(str);
            },
            //将一个类数组对象转换为真正的对象
            //results参数仅供jquery内部使用,此时在该参数的基础上添加元素
            makeArray:function(array,results){
                var ret=results||[],
                    type=jQuery.type(array);
                //undefined,null都会==null
                if(array!=null){
                    //1,没有length属性,或者具有length属性,但是是以下几种情况的
                    //2.如果array是string 的length表示字符串的长度
                    //3.如果array是函数,其length代表函数生命时的参数个数
                    //4,如果array是window对象,属性Length返回窗口中的框架(frame,iframe)个数
                    if(array.length==null|| type=='string' || type=='function' ||type=='regexp'||jQuery.isWindow(array)){
                        core_push.call(ret,array);
                    }else{//否则说明是类数组对象
                        jQuery.merge(ret,array);
                    }
                }
                return ret;
            },
              
            inArray:function(elem,array,i){
                return array==null?-1:core_indexOf.call(array,elem,i);
            },
            //用于合并两个数组的元素到第一个数组中
            //事实上,jquery源代码中第一个参数可以是数组或者类数组对象,第二个参数可以是数组、类数组对象或任何含有连续整型属性的对象
            //第一个参数是数组,最后返回数组;第一个参数是类数组,则返回类数组
            merge:function(first,second){
                var l=second.length,
                    i=first.length,
                    j;
                if(typeof l=='number'){
                    for(j=0;j<l;j++){
                        first[i++]=second[j];
                    }   
                }else{
                    while(second[j]!=undefined){
                        first[i++]=second[j++];
                    }
                }
                
                first.length=i;
                return first;
            },
            //用于查找数组中满足过滤函数的元素,形成新的数组之后返回,原数组不受影响
            //如果inv未传入或者是false,元素只有在过滤函数返回true时,才会被保存在最终的结果数组中
            //如果参数inv是true,则恰好相反
            grep:function(elems,callback,inv){
                var i,
                    ret=[],
                    length=elems.length,
                    retVal;
                inv=!!inv;
                for(i=0;i<length;i++){
                    retVal=!!callback.call(elems[i],i);
                    if(retVal!==inv){
                        ret.push(elems[i]);
                    }
                }
                return ret;             
            },
            //用于对数组中每个元素执行callback操作,并将结果形成新的数组返回
            //参数arg仅仅是jQuery内部使用
            map:function(elems,callback,arg){
                var ret=[],
                    retVal,
                    i,
                    length=elems.length,
                    isArray=isArrayLike(elems);
                if(isArray){
                    for(i=0;i<length;i++){
                        retVal=callback(elems[i],i,arg);//注意不是callback.call
                        if(retVal!=null){
                            ret.push(retVal);
                        }
                    }
                }else{
                    for(i in elems){
                        retVal=callback(elems[i],i,arg);
                        if(retVal!=null){
                            ret.push(retVal);
                        }
                    }
                }
                //保证最终返回的是一维数组
                return core_concat.call([],ret);
            },
            guid:1,
            //该方法用于更改函数的执行上下文
            //源代码中有两种传参形式,这里仅考虑最常见的一种
            proxy:function(fn,context){
                if(!jQuery.isFunction(fn)){
                    return undefined;
                }
                var args=core_slice.call(arguments,2);
                    proxy=function(){
                        return fn.call(context||this,core_concat.call(args,core_slice.call(arguments)));
                    };
                proxy.guid=fn.guid=fn.guid||jQuery.guid++;
                return proxy;
            },
            //用一个方法同时实现get和set操作
            //如何设置或者获取由回调函数fn确定
            //这个方法的实现等用到的时候结合来看
            access: function( elems, fn, key, value, chainable, emptyGet, raw ){
                
            },
            now:Date.now,
            //该方法用于交换css样式,在support模块较多用到
            //要交换的样式由参数options传递
            swap: function( elem, options, callback, args ){
                var name,ret,
                    old={};
                for(name in options){
                    old[name]=elem.style[name];
                    elem.style[name]=options[name];
                }
                ret=callback.call(elem,args||[]);
                for(name in options){
                    elem.style[name]=old[name];
                }
                return ret;
            },
            
        });
    
        //目前,js中typeof的返回值有六种:"number," "string," "boolean," "object," "function," 和 "undefined."
        //通过object.prototype.toString/或者{}.toString 返回值有九种:Boolean Number String Function Array Date RegExp Object Error,其中的Array,Date,RegExp,Object,Error都属于Object类型,在有些浏览器中typeof 正则会返回function
        jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(i,name){
            class2type["[object "+name+"]"]=name.toLowerCase();
        });
        //console.log(class2type,class2type);
        rootjQuery=jQuery(document);
    
    
        /****接下来这一部分,在jQuery的源代码中,本来是Sizzle,这里暂且略过***/
       var optionsCache={};
       function createOptions(options){
            var object=optionsCache[options]={};
            //S+,匹配非空格字符
            //正则表达式如果没有g,仅匹配第一个匹配项
            jQuery.each(options.match(/S+/g),function(i,item){
                object[item]=true;
            });
            return object;
       }
       //参数options可以是字符串或者是对象形式,可选属性/字符串组合有
       //once:回调函数列表只能执行一次
       //memory:fire调用之后,再次add将立即触发
       //unique:同一个函数不能被重复添加到回调函数列表中
       //stopOnFlase:当某一个函数返回false时,为true的时候,回调函数列表的执行终止
       jQuery.Callbacks=function(options){
            options=typeof options==='string'?optionsCache[options]||createOptions(options):options||[];
            var list=[],//用于存储回调函数列表
                firingStart,
                once=options.once,
                memory,//初始值为undefined,只有在memory模式下,调用fire后才会被赋值,以备在add中再次调用           
                fired=false,//指示是否fire过
                firingIndex,//指向要执行的下一个回调函数的下标
                add=function(arg){                
                    var type;
                    jQuery.each(arg,function(i,item){
                        type=jQuery.type(item);
                        if(type==='function'&&!(options.unique&&self.has(item))){
                            list.push(item);
                        }else if(type ==='array'){
                            add(item);
                        }
                    });                
                },
                fire=function(data){
                    fired=true;
                    memory=options.memory&&data;  
                    firingIndex=firingStart||0;             
                    firingStart=0;//在memory模式下,add的时候firingStart可能会被置为其他值,这里将其还原,以备下次调用fire的时候从头开始执行
                    var length;                    
    
                    if(!list){
                        return;
                    }
              
                    for(length=list.length;firingIndex<length;firingIndex++){
                        if(list[firingIndex].apply(data[0],data[1])===false&&options.stopOnFalse){
                            break;
                        }
                    }
                    // if(once){
                    //     if(memory){//如果通知是once和memory模式,那么在add的时候可以进行再次触发
                    //         list=[];
                    //     }else{//否则直接禁用
                    //         self.disable();
                    //     }
                    // }
    
                },
                self={
                    add:function(){
                        if(list){
                            var start=list.length;
                            add(arguments);
                            //如果是memory模式下的add,会导致立即触发
                            if(memory){//memory的初始值为undefined,memory模式下调用一次fire才会被赋值,因此第一次调用add的时候不会走下面
                                firingStart=start;
                                fire(memory);
                            }
                        }
                        return this;
                    },
                    remove:function(){
                        if(list){
                            var i;
                            jQuery.each(arguments,function(i,item){
                                //jQuery.inArray(item,list,i),返回item在list中的下表,从第i位向后数,包括第i为
                                while((i=jQuery.inArray(item,list,i))>-1){                                 
                                        list.splice(i,1);//删除上的数值                                
                                }
                            });
                        }
                        return this;
                    },               
                    //fn有值的时候,代表判断回调函数列表是否存在函数fn
                    //没有参数fn的时候,代表判断回调函数列表是否为空
                    has:function(fn){
                        return fn?jQuery.inArray(fn,list)>-1:!!(list&&list.length);
                    },                
                    empty:function(){
                        if(list){
                            list=[];
                        }
                        return this;
                    },
                    disable:function(){
                        //list就不用说了,list置为undefined之后,几乎所有的方法都不能调用
                        //memory恢复初始值undefined
                        list=memory=undefined;
                        return this;
                    },
                    disabled:function(){
                        return !list;
                    },
                    fireWith:function(context,args){
                        if(list&&!(once&&fired)){
                            args=args||[];//主要是为了处理args为undefined的情况
                            args=[context,args.slice?args.slice():args];
                            fire(args);                        
                        }
                        return this;
                    },
                    fire:function(){
                        self.fireWith(this,arguments);
                        return this;
                    },
                    fired:function(){
                        return !!fired;
                    },
                    //自己加的函数,供调试用
                    getList:function(){
                        return list;
                    }
                };
                return self;
       };
    
       //实现异步队列Defered,When
       //异步队列内部维护了三个回调函数列表,分别是成功,失败,消息
       jQuery.extend({
            //func参数仅内部使用,func的调用者是jQuery.Deferred的返回值,参数也是
            Deferred:function(func){
                var doneList=jQuery.Callbacks('once memory'),
                    failList=jQuery.Callbacks('once memory'),
                    progressList=jQuery.Callbacks('memory'),
                    state='pending',
                    list={
                        'resolve':doneList,
                        'reject':failList,
                        'notify':progressList
                    },
                    promise={
                        done:doneList.add,
                        fail:failList.add,
                        progress:progressList.add,                    
                        state:function(){
                            return state;
                        },
                        //同时添加成功,失败,消息回调函数
                        then:function(doneCallback,failCallback,progressCallback){
                            deferred.done(doneCallback).fail(failCallback).progress(progressCallback);
                        },
                        //成功,失败时,添加同一个处理函数
                        always:function(){
                            deferred.done(arguments).fail(arguments);
                        },
                        //说实话,能看懂这个源代码,但搞不太懂这个pipe是干嘛用的
                        //实际使用中调用的地方也不多
                        //不过其源代码有不少知识点值得学习
                        pipe:function(fnDone,fnFail,fnProgress){
                            //这里的newDefer,就是调用jQuery.Deferred(function(newDeferred))返回的异步队列对象,由这部分代码最终的func.apply(deferred,deferred)决定;
                            return jQuery.Deferred(function(newDefer){
    
                                jQuery.each({
                                    done:[fnDone,'resolve'],
                                    fail:[fnFail,'reject'],
                                    progress:[fnProgress,'notify']                              
                                },function(handler,data){
                                    //注意这三个局部变量定义的位置,只能定义在该闭包中,如果定义在jQuery.Deferred得到的只是函数最后的值,如果没有传递fnProgress,就会报出undefined的错误
                                    var action=data[1],
                                        fn=data[0],
                                        returned;
                                    if(jQuery.isFunction(fn)){
                                        //通过done,fail,progress添加的方法,只有在对应的回调函数队列fire的时候才会触发
                                        deferred[handler](function(){
                                            //这里的this,arguments是调用fire/fireWith时候传递
                                            //这里的this可以通过fireWith中指定context,arguments也是fire/fireWith的时候传递的参数
                                            
                                            returned=fn.apply(this,arguments);
                                            //如果函数的返回值依旧是一个异步队列,则将jQuery.pipe返回的异步队列的成功,失败,消息回调添加到返回的retuned对应的回调列表中
                                            if(returned&&jQuery.isFunction(returned.promise)){
                                                returned.promise().then(newDefer.resolve,newDefer.reject,newDefer.notify);
                                            }else{
                                                //如果函数返回值不是异步队列,则jQuery.pipe()返回的异步队列对应状态的方法立即触发
                                                newDefer[action+'With'](this===deferred?newDefer:this,[returned]);
                                            }
                                        });
                                    }else{
                                        deferred[handler](newDefer[action]);
                                    }
    
                                });
                            }).promise();
                        },
                        //注意promise()和promise({})这两种写法是完全不同的,前者返回异步对象的只读版本,后者返回一个副本
                        promise:function(obj){
                            return obj==null?promise:jQuery.extend(obj,promise);
                        },  
                    },
                    deferred=promise.promise({}),
                    key;
                    //为deferred添加状态改变的相关函数,与fire,fireWith相对应
                for(key in list){
                    deferred[key]=list[key].fire;
                    deferred[key+'With']=list[key].fireWith;
                }
                deferred.done(function(){
                    state='resolved';
                },failList.disable,progressList.disable)
                .fail(function(){
                    state='rejected';
                },doneList.disable,progressList.disable);
    
                    
                if(func){
                    //这句话决定了,通过jQuery.Deferred(func)调用的时候,func的context和参数
                    func.call(deferred,deferred);
                }
                return deferred;
    
            },
        
       });
    
    
        window.jQuery=window.$=jQuery;
    })(window);
    myJqury.js

     jQuery源代码中的实现:

    jQuery.extend({
    
        Deferred: function( func ) {
            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 ) {
                            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;
        },
    
        // Deferred helper
        when: function( subordinate /* , ..., subordinateN */ ) {
            var i = 0,
                resolveValues = core_slice.call( arguments ),
                length = resolveValues.length,
    
                // the count of uncompleted subordinates
                remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
    
                // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
                deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
    
                // Update function for both resolve and progress values
                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;
    
            // add listeners to Deferred subordinates; treat others as resolved
            if ( length > 1 ) {
                progressValues = new Array( length );
                progressContexts = new Array( length );
                resolveContexts = new Array( length );
                for ( ; i < length; i++ ) {
                    if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
                        resolveValues[ i ].promise()
                            .done( updateFunc( i, resolveContexts, resolveValues ) )
                            .fail( deferred.reject )
                            .progress( updateFunc( i, progressContexts, progressValues ) );
                    } else {
                        --remaining;
                    }
                }
            }
    
            // if we're not waiting on anything, resolve the master
            if ( !remaining ) {
                deferred.resolveWith( resolveContexts, resolveValues );
            }
    
            return deferred.promise();
        }
    });
    jQuery—Defered
  • 相关阅读:
    IO 单个文件的多线程拷贝
    day30 进程 同步 异步 阻塞 非阻塞 并发 并行 创建进程 守护进程 僵尸进程与孤儿进程 互斥锁
    day31 进程间通讯,线程
    d29天 上传电影练习 UDP使用 ScketServer模块
    d28 scoket套接字 struct模块
    d27网络编程
    d24 反射,元类
    d23 多态,oop中常用的内置函数 类中常用内置函数
    d22 封装 property装饰器 接口 抽象类 鸭子类型
    d21天 继承
  • 原文地址:https://www.cnblogs.com/bobodeboke/p/5994453.html
Copyright © 2011-2022 走看看