回调对象Callbacks就是用来管理回调函数队列的。
它提供几个便捷的处理参数
- once: 确保这个回调列表只执行一次
- memory: 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调
- unique: 确保一次只能添加一个回调(所以在列表中没有重复的回调).
- stopOnFalse: 当一个回调返回false 时中断调用
once和stopOnFalse作用于fire
memory和unique作用于add
once在源码中的实现:
// 以给定的上下文和参数调用所有回调函数
fireWith: function( context, args ) {
//第二次触发的时候fired为true
//stack = !options.once && []
//!fired为false
//如果没有设置once,!options.once && []为true 则再次触发fire( args );
//如果设置了once,!options.once && []为false 则不会再次触发fire( args );
if ( list && ( !fired || stack ) ) {
args = args || [];
//args.slice是判断args是不是数组
args = [ context, args.slice ? args.slice() : args ];
if ( firing ) {//如果正在回调
//将参数推入堆栈,等待当前回调结束再调用
stack.push( args );
} else {//否则直接调用
fire( args );
}
}
return this;
}
memory在源码中的实现:
// 存在memory就会再次触发fire
else if ( memory ) {
//如果options.memory为true,则将memory做为参数,应用最近增加的回调函数
firingStart = start;
fire( memory );
}
unique在源码中的实现:
options.unique->如果没有设置unique,不管list中有没有该回调都可以添加
options.unique->如果设置了unique,只有list中没有才可以添加该回调
if ( !options.unique || !self.has( arg ) ) {//确保是否可以重复
list.push( arg );
}
stopOnFalse在源码中的实现:
//data[ 0 ]是执行环境
//data[ 1 ]就是fire中的参数
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
//阻止未来可能由于add所产生的回调
memory = false;
break;//由于options.stopOnFalse设置为true,所以当有回调函数返回值为false时退出循环
}
once_demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>once</title>
</head>
<body>
<script type="text/javascript" src="../jquery-2.1.1.js"></script>
<script type="text/javascript"> var cb = $.Callbacks("once"); function aaa(){ console.log(1); } function bbb(){ console.log(2); } cb.add(aaa); cb.add(bbb); cb.fire();//输出1 cb.fire();//不执行 </script>
</body>
</html>
memory_demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>memory</title>
</head>
<body>
<script type="text/javascript" src="../jquery-2.1.1.js"></script>
<script type="text/javascript"> var cb = $.Callbacks("memory"); function aaa(){ console.log(1); } function bbb(){ console.log(2); } cb.add(aaa); cb.fire(); cb.add(bbb);//由于memory有记忆功能,后添加的也能触发 </script>
</body>
</html>
unique_demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Unique</title>
</head>
<body>
<script type="text/javascript" src="../jquery-2.1.1.js"></script>
<script type="text/javascript"> var cb = $.Callbacks("unique"); function aaa(){ console.log(1); } cb.add(aaa);//有效 cb.add(aaa);//添加不进去 cb.fire();//输出 1 </script>
</body>
</html>
源码处理解析:
if ( !options.unique || !self.has( arg ) ) { //确保是否可以重复
list.push( arg );
}
如果options.unique为true,并且回调列表中不包含arg,则将arg添加到回调列表中
stopOnFalse_demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>stopOnFalse</title>
</head>
<body>
<script type="text/javascript" src="../jquery-2.1.1.js"></script>
<script type="text/javascript"> var cb = $.Callbacks("stopOnFalse"); function aaa(){ console.log(1); return false; } function bbb(){ console.log(2); } cb.add(aaa);//执行输出1 cb.add(bbb);//不执行 cb.fire(); </script>
</body>
</html>
混合参数_demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>混合参数</title>
</head>
<body>
<script type="text/javascript" src="../jquery-2.1.1.js"></script>
<script type="text/javascript"> var cb = $.Callbacks("once memory"); function aaa(){ console.log(1); } function bbb(){ console.log(2); } cb.add(aaa); cb.fire();//执行输出1 2 cb.add(bbb); cb.fire();//因为once,不执行 </script>
</body>
</html>
根据jQuery.Callbacks()的API:
延迟对象是基于回调对象来实现的,用来实现异步的统一管理
when()是用来辅助延迟对象的
大体框架如下:
jQuery.extend({//两个工具方法
Deferred:function(){},
when : function(){}
});
有三组对应关系:
前两组对应关系,分别对应相应的状态变化:
- pending->resolved
- pending->rejected
一旦发生两种状态变化中的一种,则状态变为不可变
<body>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript"> var cb = $.Callbacks(); setTimeout(function(){ alert(111); cb.fire(); },1000); //add先把这个函数存到数组里 //当调用fire的时候再触发 cb.add(function(){ alert(222); }); </script>
<script type="text/javascript"> //延迟对象-实现对异步的统一管理 var dfd = $.Deferred();//延迟对象 setTimeout(function(){ alert(111); dfd.resolve(); },1000); dfd.done(function(){ alert(222); }); </script>
<script type="text/javascript"> setTimeout(function(){ alert(111); },1000); //定时器就是异步的 //延迟对象可以实现从上往下的顺序 //先弹出1,再弹出2 alert(222); </script>
<script type="text/javascript"> var dfd = $.Deferred(); setTimeout(function(){ alert(111); dfd.reject(); },1000); dfd.fail(function(){ alert(222); }); </script>
<script type="text/javascript"> var dfd = $.Deferred(); setTimeout(function(){ alert(111); dfd.notify(); },1000); dfd.progress(function(){ alert(222); }); </script>
</body>
<body>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript"> var def = $.Deferred(); setInterval(function(){ alert(111); def.resolve(); // def.reject(); // def.notify(); },1000); def.done(function(){ alert("成功");//只会弹一次成功 //因为源码中设置了jQuery.Callbacks("once memory") }).fail(function(){ alert("失败");//只会弹一次失败 //因为源码中设置了jQuery.Callbacks("once memory") }).progress(function(){ alert("进度中");//每隔一秒都会弹出 }); </script>
</body>
Deferred对象和promise对象都是延迟对象
通过上面的列举,我们可以看出:deferred比promise多了resolve、reject和notify
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript"> function aaa(){ var dfd = $.Deferred(); setTimeout(function(){ dfd.resolve();//不会触发 },1000); return dfd; } var newDfd = aaa(); newDfd.done(function(){ alert("成功"); }).fail(function(){ alert("失败");//弹出失败 }); newDfd.reject();//会先触发 </script>
</body>
dfd.promise():延迟对象调用promise()方法,返回一个promise对象
promise对象中没有reject()方法
这里为了阻止延迟对象的状态在外部被改变,需要这样来调用dfd.promise()
源码中是这样实现的:
dfd.promise()这里参数为空,所以这里就返回promise对象
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript"> function aaa(){ var dfd = $.Deferred(); setTimeout(function(){ dfd.resolve(); },1000); return dfd.promise(); } var newDfd = aaa(); newDfd.done(function(){ alert("成功");//弹出成功 }).fail(function(){ alert("失败"); }); newDfd.reject();//会报错 </script>
</body>
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript"> var dfd = $.Deferred(); setTimeout(function(){ //dfd.resolve(); dfd.reject(); },1000); //不管是resolve还是reject,always总会执行 dfd.always(function(){ alert("总会弹出"); }); </script>
</body>
源码中是这样写的:
always: function() {
//总会触发是因为既有done又有fail
deferred.done( arguments ).fail( arguments );
return this;
//return this表示可以进行链式操作
}
返回状态
resovled或者rejected
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript"> function aaa(){ var dfd=$.Deferred(); alert(dfd.state());//pending setTimeout(function(){ dfd.resolve();//resolve()之后,状态变为resolved alert(dfd.state());//resolved },1000); return dfd; } var newDfd=aaa(); newDfd.done(function(){ alert('成功'); }).fail(function(){ alert('失败'); }); </script>
</body>
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript"> var dfd = $.Deferred(); setTimeout(function(){ //dfd.resolve(); dfd.reject(); },1000); //then中对应三种状态 dfd.then(function(){ alert("成功"); },function(){ alert("失败"); },function(){ alert("进行中"); }); </script>
</body>
// then和pipe代码一样,功能不一样
// pipe可以把延迟对象变长
promise.pipe = promise.then;
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript"> var dfd = $.Deferred(); setTimeout(function(){ //dfd.resolve(); dfd.resolve('hello');//可以接收参数传递 },1000); var newDfd = dfd.pipe(function(){ return arguments[0]+'你好'; }); newDfd.done(function(){ alert(arguments[0]);//hello你好 }); </script>
</body>
when是用来辅助延迟对象的工具方法
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript"> function aaa(){ var dfd = $.Deferred(); //dfd.resolve(); dfd.reject(); return dfd; } function bbb(){ var dfd = $.Deferred(); dfd.resolve(); //dfd.reject(); return dfd; } /* 这样写只有aaa()和bbb()同时完成触发才弹出,但是任意一个未完成就可以触发fail() */ //when是针对多个延迟对象的 $.when(aaa(),bbb()).done(function(){ alert('成功'); }).fail(function(){ alert('失败'); }); </script>
</body>
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript"> function aaa(){ var dfd = $.Deferred(); dfd.resolve(); return dfd; } function bbb(){ var dfd = $.Deferred(); dfd.reject(); return dfd; //这里注销后将返回成功,不注销返回失败 } /* 这样写只有aaa()和bbb()同时完成触发才弹出,但是任意一个未完成就可以触发fail() */ //when是针对多个延迟对象的 //when中的参数必须是延迟对象才起作用 //如果不是延迟对象就会自动跳过该参数 $.when(aaa(),bbb()).done(function(){ alert('成功'); }).fail(function(){ alert('失败'); }); </script>
</body>
$.when(aaa(),bbb(),ccc(),ddd()).done(function(){
alert('成功');
});
aaa()->arguments[0] done()
bbb()->arguments[1] done()
ccc()->arguments[2] done()
ddd()->arguments[3] done()
when是针对多个延迟对象的,比如这里针对四个延迟对象,when的源码中有一个计数器,这里计数器就是4,触发一个延迟对象,计数器减1,当计数器为0时。$.when()会返回一个延迟对象deferred.promise(),然后触发resolve()方法,最后触发done。