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[],
fn=data[],
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:,
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[]);
if(elem&&elem.parentNode){
this[]=elem;
this.length=;
}
this.selector=selector;
this.context=document;
return this;
}else{
//说明是复杂的选择器表达式,这里暂不考虑
}
}
//处理selector是DOM元素的情形
if(selector&&selector.nodeType){
this[]=selector;
this.length=;
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<?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();
},
last:function(){
return this.get(-);
},
eq:function(i){
var length=this.length,
j=+i+(i<?length:);
return this.pushStack(j>=&&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[]||{},//指向目标对象
deep=false,//是否进行深度复制
i=,//表示源对象的起始下标
length=arguments.length,//表示参数个数;
options,name,src,copy,copyIsArray;//options指向某个源对象,name指向源对象的某个属性名,src目标对象某个属性的原始值,copy某个源对象的某个属性的值,copyIsArray指示变量copy是否为数组
//首先进行参数修正
if(typeof target==='boolean'){
deep=target;
target=arguments[]||{};
i=;
}
//此时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&©&&(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===&&length){
return true;
} if(type==='array'){
return true;
}
if(typeof length==='number'&&(length==||(length>&&(length-) 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:,
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=;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=;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?-: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=;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=;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=;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:,
//该方法用于更改函数的执行上下文
//源代码中有两种传参形式,这里仅考虑最常见的一种
proxy:function(fn,context){
if(!jQuery.isFunction(fn)){
return undefined;
}
var args=core_slice.call(arguments,);
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||;
firingStart=;//在memory模式下,add的时候firingStart可能会被置为其他值,这里将其还原,以备下次调用fire的时候从头开始执行
var length; if(!list){
return;
} for(length=list.length;firingIndex<length;firingIndex++){
if(list[firingIndex].apply(data[],data[])===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))>-){
list.splice(i,);//删除上的数值
}
});
}
return this;
},
//fn有值的时候,代表判断回调函数列表是否存在函数fn
//没有参数fn的时候,代表判断回调函数列表是否为空
has:function(fn){
return fn?jQuery.inArray(fn,list)>-:!!(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[],
fn=data[],
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[ ],
fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
// deferred[ done | fail | progress ] for forwarding actions to newDefer
deferred[ tuple[] ](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[ ],
stateString = tuple[ ]; // promise[ done | fail | progress ] = list.add
promise[ tuple[] ] = list.add; // Handle state
if ( stateString ) {
list.add(function() {
// state = [ resolved | rejected ]
state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock
}, tuples[ i ^ ][ ].disable, tuples[ ][ ].lock );
} // deferred[ resolve | reject | notify ]
deferred[ tuple[] ] = function() {
deferred[ tuple[] + "With" ]( this === deferred ? promise : this, arguments );
return this;
};
deferred[ tuple[] + "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 = ,
resolveValues = core_slice.call( arguments ),
length = resolveValues.length, // the count of uncompleted subordinates
remaining = length !== || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : , // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
deferred = remaining === ? 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 > ? 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 > ) {
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
javascript源代码学习之五——jQuery.deferred的更多相关文章
- jQuery源代码学习之五——jQuery.when
jQuery.when提供了基于一个或多个对象的状态来执行回调函数的功能,通常是基于具有异步事件的异步队列. 如果传入多个异步队列,jQuery.when会返回一个新的主异步队列的只读副本(promi ...
- jQuery源代码学习之八——jQuery属性操作模块
一.jQuery属性模块整体介绍 jQuery的属性操作模块分四个部分:html属性操作,dom属性操作,类样式操作,和值操作. html属性操作(setAttribute/getAttribute) ...
- jQuery源代码学习之六——jQuery数据缓存Data
一.jQuery数据缓存基本原理 jQuery数据缓存就两个全局Data对象,data_user以及data_priv; 这两个对象分别用于缓存用户自定义数据和内部数据: 以data_user为例,所 ...
- jQuery源代码学习之四——jQuery.callbacks
自己实现的callbacks模块相较于jquery源代码中的callbacks模块有所简化,表面上看没有考虑firing这个参数,没有对之进行任何处理,即没有考虑在函数执行过程中,再次调用add,re ...
- javascript设计模式学习之五——策略模式
一.策略模式定义: 定义一些列的算法/规则,将它们封装起来,使得它们可以互相替换/组合使用.其目的在于将算法/规则封装起来,将算法/规则的使用与实现分离出来. 通过策略模式,可以减少算法计算过程中大量 ...
- jQuery源代码学习之九—jQuery事件模块
jQuery事件系统并没有将事件坚挺函数直接绑定在DOM元素上,而是基于事件缓存模块来管理监听函数的. 二.jQuery事件模块的代码结构 //定义了一些正则 // // //jQuery事件对象 j ...
- jQuery源代码学习之七—队列模块queue
一.jQuery种的队列模块 jQuery的队列模块主要是为动画模块EFFECTS提供支持,(不过到现在为了支持动画队列的inprogress的出入队还是搞不太清楚),单独抽取出一个命名空间是为了使程 ...
- javascript异步代码的回调地狱以及JQuery.deferred提供的promise解决方式
我们先来看一下编写AJAX编码常常遇到的几个问题: 1.因为AJAX是异步的,全部依赖AJAX返回结果的代码必需写在AJAX回调函数中.这就不可避免地形成了嵌套.ajax等异步操作越多,嵌套层次就会越 ...
- jQuery源代码学习_工具函数_type
jquery源代码学习_工具函数_type jquery里面有一个很重要的工具函数,$.type函数用来判断类型,今天写这篇文章,是来回顾type函数的设计思想,深入理解. 首先来看一下最终结果: 上 ...
随机推荐
- 对只转发结果集的无效操作 first
今天只用jdbc连接Oracle查询结果时,出现了一个: 对只转发结果集的无效操作 first 的错误java.sql.sqlexception. 出现这个结果的原因是:使用 Statement st ...
- 转:45 个 LoadRunner 面试问题(附答案)_纯英文,太有逼格了
What is load testing? - Load testing is to test that if the application works fine with the loads th ...
- Code[VS]1021 玛丽卡题解
Code[VS]1021 玛丽卡题解 SPFA Algorithm 题目传送门:http://codevs.cn/problem/1021/ 题目描述 Description 麦克找了个新女朋友,玛丽 ...
- POJ 2402 Palindrome Numbers
题目链接 水题,LA我居然没找到在那里. #include <cstdio> #include <cstring> #include <string> #inclu ...
- Mybatis一级、二级缓存
Mybatis一级.二级缓存 一级缓存 首先做一个测试,创建一个mapper配置文件和mapper接口,我这里用了最简单的查询来演示. <mapper namespace="c ...
- Java生成CSV文件实例详解
本文实例主要讲述了Java生成CSV文件的方法,具体实现步骤如下: 1.新建CSVUtils.java文件: package com.saicfc.pmpf.internal.manage.utils ...
- navicat远程连接mysql
转载:http://blog.sina.com.cn/s/blog_84485e540101178p.html ERROR 1130: Host '192.168.1.81' is not a ...
- mysql体系结构
mysql逻辑架构: 第一层,即最上一层,所包含的服务并不是MySQL所独有的技术.它们都是服务于C/S程序或者是这些程序所需要的:连接处理,身份验证,安全性等等. 第二层值得关注.这是MySQL的核 ...
- MySQL数据库基本指令
对MySQL的指令不太熟悉,在此特别整理了一下一些常用的指令: 约定:大写字母写SQL关键字和函数名,小写字母写数据库.数据表和数据列的名字.(下述代码更新不同步,部分代码未依据此约定) 1 数据库的 ...
- print_r、echo、var_dump三者的区别
这三者均是php里输出的方式. 1.print_r用于输出数组. 2.echo只能用于输出字符串,在没有框架的时候,多用这种方法将结果打印在html页面上. 3.var_dump能输出任何类型的数据, ...