目录
part1 deferred延迟对象
part2 when辅助方法
网盘源代码链接: https://pan.baidu.com/s/1skAj8Jj 密码: izta
part1 deferred延迟对象
1 . deferred基于callbacks开发
使用callbacks完成异步:
<script src="js/jquery-2.0.3.js"></script> ???<script> ???????var cb=$.Callbacks(); ???????setTimeout(function(){ ???????????alert(111); ???????????cb.fire(); ???????},1000); ???????cb.add(function(){ ???????????alert(222); ???????}) ???????//通过回调,先弹出111,再弹出222. ???</script>
使用deferred完成异步:
<script src="js/jquery-2.0.3.js"></script> ??<script> ??????var dfd=$.Deferred(); ??????setTimeout(function(){ ??????????alert(111); ??????????dfd.resolve(); ??????}) ??????dfd.done(function(){ ??????????alert(222); ??????}) ??????//通过延迟对象,先弹出111,再弹出222. ??</script>
我们看到,两者代码非常相似,其实deferred延迟对象本就是基于callbacks来开发的。deferred的resolve对应callbacks的fire,deferred的done对应callbacks的add。
延迟对象除了resolve状态,还有很多其他一些状态。
//3046行,这里定义了3套状态//resolve代表成功,reject代表失败,notify代表通知与过程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") ] ???????????],
类似:
<script src="js/jquery-2.0.3.js"></script><script> ???var dfd=$.Deferred(); ???setTimeout(function(){ ???????alert(111); ???????dfd.reject(); ???}) ???dfd.fail(function(){ ???????alert(222); ???}) ???//延迟失败,调用fail方法,那么最终的结果是先弹出111,再弹出222.</script>
ajax内置了deferred对象哦。
<script src="js/jquery-2.0.3.js"></script> ????<script> ????????$.ajax("xxx.php").done(function(){ ????????????alert("成功!") ????????}).fail(function(){ ????????????alert("失败"); ????????}) ????</script>
2.延迟对象的状态映射
我们知道deferred的成功状态包含resolve和done方法,失败状态包含reject和fail方法,进度中包含notify和progress方法。那么在源码中是如何映射起来的呢。
1.状态映射
//把相同状态的方法名都放到同一个数组里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") ] ???????????],
//遍历数组,得到的元素也是一个数组,赋值给list ???????jQuery.each( tuples, function( i, tuple ) { ???????????var list = tuple[ 2 ], ???????????????stateString = tuple[ 3 ]; ???????????????????????//状态字符串,即resolved和rejected。 ???????????// promise[ done | fail | progress ] = list.add ???????????promise[ tuple[1] ] = list.add; ???????????????????????//把done、fail添加到add方法里 ???????????// 处理状态字符串 ???????????if ( stateString ) { ???????????????????????// ???????????????list.add(function() { ???????????????????// 状态字符串,即resolved和rejected ???????????????????state = stateString; ???????????????// 一旦发生某个状态,那么对应的fire就会被禁用,或者progress就会上锁。 ???????????????}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); ???????????} ???????????// deferred[ resolve | reject | notify ]使用了callbacks的fire触发 ???????????deferred[ tuple[0] ] = function() { ???????????????deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); ???????????????return this; ???????????}; ???????????deferred[ tuple[0] + "With" ] = list.fireWith; ???????});
2.once和memory功能
先来看callbacks。
<body> ??<button >按钮</button><script src="js/jquery-2.0.3.js"></script><script> ?var cb=$.Callbacks("memory"); ?cb.add(function(){ ?????alert("aaa"); ?}); ?cb.fire(); ?$("#btn").click(function(){ ?????alert("bbb"); ?}) ???//当我们点击按钮时,因为memory的记忆功能,点击按钮依然能弹出bbb。</script></body>
那么类似的,deferred也有memory功能,参照3048行的代码,jQuery.Callbacks("once memory")。我们使用progress/notify一组来测试。
<script src="js/jquery-2.0.3.js"></script><script> ???var dfd=$.Deferred(); ???setInterval(function(){ ???????alert(111); ???????dfd.notify(); ???},1000); ???dfd.done(function(){ ???????alert("成功"); ???}).fail(function(){ ???????alert("失败") ???}).progress(function(){ ???????alert("进度中") ???}); ???//每隔一秒就会执行alert 111的任务,同时由于deferred的记忆功能,所以也会反复弹出"进度中"。</script>
除了memory,那么once的意思是只有一次。
<script src="js/jquery-2.0.3.js"></script><script> ???var dfd=$.Deferred(); ???setInterval(function(){ ???????alert(111); ???????dfd.resolve(); ???},1000); ???dfd.done(function(){ ???????alert("成功"); ???}).fail(function(){ ???????alert("失败") ???}).progress(function(){ ???????alert("进度中") ???}); ???//无论resolve每隔一秒执行多少次,最终的done方法也只执行一次。</script>
3.promise对象
1.promise对象与deferred对象的关系
在3095行开始的jQuery.each( tuples, function( i, tuple ) {each方法里,会把promise[ done | fail | progress ] = list.adddone、fail、progress方法添加到callbacks的add方法里,这里使用了promise对象。3113行的deferred[ tuple[0] ] = function() {则使用了deferred对象。那么两者之间的关系是什么呢?
promise: function( obj ) { ???return obj != null ? jQuery.extend( obj, promise ) : promise; ???????????????}//...promise.promise( deferred );
我们从这些源码里可以看到,promise会继承给deferred对象。
promise = { ???????????????state: function() { ???????????????????return state; ???????????????},//.......
promise定义的方法包括state、always、then、promise、pipe、done、fail、progress等等,deferred定义的包括resolve、reject、notify方法,那么通过继承,deferred也就可以拥有所有的方法了。那么区别就是多出来的resolve、reject、notify方法。
这里有一个应用:
<script src="js/jquery-2.0.3.js"></script><script> ???function aaa(){ ???????var dfd=$.Deferred(); ???????setTimeout(function(){ ???????????dfd.resolve(); ???????},1000); ???????return dfd; ???} ???var dfd=aaa(); ???dfd.done(function(){ ???????alert("成功"); ???}).fail(function(){ ???????alert("失败"); ???}); ???dfd.reject(); ???//由于reject的修改,那么弹出失败</script>
可是如果我们返回的是promise对象,那么是没有暴露resolve或者reject等方法的,所以也就不存在可以修改状态了。
<script src="js/jquery-2.0.3.js"></script><script> ???function aaa(){ ???????var dfd=$.Deferred(); ???????setTimeout(function(){ ???????????dfd.resolve(); ???????},1000); ???????return dfd.promise(); ???} ???var dfd=aaa(); ???dfd.done(function(){ ???????alert("成功"); ???}).fail(function(){ ???????alert("失败"); ???}); ???dfd.reject();//报错 ???//弹出成功字符串</script>
4.state状态的控制
state = "pending",//一开始state就是pending“进行中” ???????????promise = { ???????????????state: function() { ???????????????????return state; ???????????????},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 ); ???????????}
1.首先,来演示state的各个状态。
<script src="js/jquery-2.0.3.js"></script> ?<script> ?????function aaa(){ ?????????var dfd=$.Deferred(); ?????????alert(dfd.state()); ?????????//首先弹出deferred对象的状态 ?????????setTimeout(function(){ ?????????????dfd.resolve(); ?????????????alert(dfd.state()); ?????????//隔一秒之后已经成功了,那么弹出状态resolved ?????????},1000); ?????????return dfd.promise(); ?????} ?????var dfd=aaa(); ?????dfd.done(function(){ ?????????alert("成功"); ?????}).fail(function(){ ?????????alert("失败"); ?????}); ?</script>
结合deferred对象后面的代码,整个执行结果是先弹出pending,然后弹出字符串“成功”,最后弹出resolved。为什么使用的是state方法,其实源代码里揭示的十分清楚,它是一个function。
state: function() {//3054行
2.接下来看源代码是如何控制状态的改变的。
var tuples = [ ???????????????????????????[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], ???????????????[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], ???????????????[ "notify", "progress", jQuery.Callbacks("memory") ] ???????????],
我们看到,只有resolve和reject才有更新的状态哦,所以对于notify状态来说是没有stateString的,所以也就不会走if语句。
jQuery.each( tuples, function( i, tuple ) { ???????????var list = tuple[ 2 ], ???????????????stateString = tuple[ 3 ];//第4个元素正好是状态字符串 ???????????promise[ tuple[1] ] = list.add; ???????????// 处理状态字符串 ???????????if ( stateString ) {//... ???????????????}
3.最后看重要的if语句是如何控制的。
if ( stateString ) { ???????????????list.add(function() { ???????????????????// 将[ resolved | rejected ]赋值给state ???????????????????state = stateString; ???????????????}, tuples[ i ^ 1 ][ 2 ].disable, ????????????????????????????????//因为tuple第3个元素就是回调jQuery.callbacks,那么禁用掉 ??????????????????????????????tuples[ 2 ][ 2 ].lock ); ??????????????????????????????//第3个元素也就是关于notify的, ????????????????????//那么再数第3个元素也是回调jQuery.callbacks,锁住,就不会触发了 ???????????}
可以看到通过disable来禁用resolve和reject的回调,以及锁住notify的状态,那么状态一旦发生就不会改变了。
4.补充知识:
位运算符tuples[ i ^ 1 ][ 2 ]。
jQuery.each( tuples, function( i, tuple ) {//3095行,说明i取值为0和1
<script> ??????alert(1^1);//弹出0 ??????alert(0^1);//弹出1 ??</script>
那么也就是说如果i为0,那么取值为1,如果i为1,那么取值为0。所以对于禁用的原则是遍历到谁,那么其他的禁用(有点取反的意思在哦)。
4.deferred对象的工具方法always()、then()
1.always方法
always: function() { ???????????????????deferred.done( arguments ).fail( arguments ); ???????????????????return this; ???????????????},//源码对deferred对象,done方法、fail方法都调用传入的回调
那么看一个例子,就非常清晰了。
<script src="js/jquery-2.0.3.js"></script><script> ???function aaa(){ ???????var dfd=$.Deferred(); ???????setTimeout(function(){ ???????????dfd.reject(); ???????},1000); ???????return dfd.promise(); ???} ???var dfd=aaa(); ???dfd.always(function(){ ???????alert("无论状态如何,调用这个回调函数"); ???});</script>
2.then方法
3.promise方法
part2 when方法
1.when的使用
when方法在源码里是一个辅助方法。它的使用和deferred延迟对象是有区别的,deferred只能针对一个延迟对象进行操作,when能针对多个延迟对象进行判断。
1.when方法的延迟对象同时成功时触发done方法。
DEMO演示:
<script src="js/jquery-2.0.3.js"></script><script> ???function aaa(){ ???????var dfd=$.Deferred(); ???????dfd.resolve(); ???????return dfd; ???} ???function bbb(){ ???????var dfd=$.Deferred(); ???????dfd.resolve(); ???????return dfd; ???} ???$.when(aaa(),bbb()).done(function(){ ???????alert("成功!"); ???})</script>
浏览器显示结果:
2.when方法只要有一个延迟对象的状态是失败,那么就触发fail函数。
<script src="js/jquery-2.0.3.js"></script><script> ???function aaa(){ ???????var dfd=$.Deferred(); ???????dfd.resolve(); ???????return dfd; ???} ???function bbb(){ ???????var dfd=$.Deferred(); ???????dfd.reject(); ???????return dfd; ???} ???$.when(aaa(),bbb()).done(function(){ ???????alert("成功!"); ???}).fail(function(){ ???????alert("失败"); ???})</script>
浏览器显示结果:
3.参数处理
无参、基本类型(比如123)、普通的函数并没有返回promise,那么就直接跳过参数。
<script src="js/jquery-2.0.3.js"></script><script> ???$.when(123).done(function(){ ???????alert("成功!"); ???}).fail(function(){ ???????alert("失败"); ???}) ???//基本数据类型123就直接跳过啦,那么弹出成功字符串</script>
那么既然一定要一个满足返回promise的函数,为什么要这样做呢,好处是能够传参。
<script src="js/jquery-2.0.3.js"></script><script> ???$.when(123).done(function(){ ???????alert(arguments[0]); ???????alert("成功!"); ???}).fail(function(){ ???????alert("失败"); ???}); ???//弹出123字符串和成功字符串。</script>
2.when方法的源码设计思想
我们先来假设一个例子,它有4个延迟对象,那么返回的是return deferred.promise();//3181行,所以promise可以使用done方法。那么具体怎么做的呢。
$.when(aaa(),bbb(),ccc(),ddd()).done(function(){ ???????alert("成功!"); ???});
先看一个图示。
3.when()无参的情况
DEMO演示:
<script src="js/jquery-2.0.3.js"></script><script> ???$.when().done(function(){ ???????alert("成功!"); ???}).fail(function(){ ???????alert("失败"); ???}) ???//因为无参,那么跳过,最终弹出成功字符串。</script>
进入源码,3134行,
var i = 0, ???????????resolveValues = core_slice.call( arguments ),//这里处理的是,往when方法传基本数据的方式 ???????????length = resolveValues.length,//length就是传入的参数个数,那么无参就是0咯 ???????????????????remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,//由于length取0,那么说参数没有那么就取length也就是0.所以remaining计数器为0
计数器remaining为0,length为0.
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),es//也就不用执行咯 ???????if ( length > 1 ) { ???????}//if语句进不去
???????if ( !remaining ) { ???????????deferred.resolveWith( resolveContexts, resolveValues ); ???????}//remaining为0,进入if语句,所以deferred就被resolve了 ???????return deferred.promise();
既然deferred被resolve了,那么关于$.when().done(function(){就可以被执行了。
总之,如果无参,按照resolve来考虑。
4.when()的参数是基本类型,比如123
DEMO演示:
<script src="js/jquery-2.0.3.js"></script><script> ???$.when(123).done(function(){ ???????alert("成功!"); ???}).fail(function(){ ???????alert("失败"); ???}) ???//基本数据类型123就直接跳过啦,那么弹出成功字符串</script>
进入源码,那么length为1,
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,//有参数,而且判断非promise函数,所以remaining计数器取0
接下来的updateFunc、length>1的if语句都不会走。!remaining的if语句会进去resolve。
if ( !remaining ) { ???????????deferred.resolveWith( resolveContexts, resolveValues ); ???????} ???????return deferred.promise();
5.when方法的普通函数的参数
DEMO展示:
<script src="js/jquery-2.0.3.js"></script><script> ???function aaa(){ ???????var dfd=$.Deferred(); ???????dfd.resolve(); ???????//即使定义了延迟对象,可是并没有返回promise对象 ???} ???$.when(aaa()).done(function(){ ???????alert("成功!"); ???}).fail(function(){ ???????alert("失败"); ???}) ???//普通函数就直接跳过</script>
进入源码,那么length为1,
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,//有参数,而且判断非promise函数,所以remaining计数器取0
接下来的updateFunc、length>1的if语句都不会走。!remaining的if语句会进去resolve。
if ( !remaining ) { ???????????deferred.resolveWith( resolveContexts, resolveValues ); ???????} ???????return deferred.promise();
4.when方法只返回一个promise
DEMO展示:
<script src="js/jquery-2.0.3.js"></script><script> ???function aaa(){ ???????var dfd=$.Deferred(); ???????dfd.resolve(); ???????return dfd; ???} ???$.when(aaa()).done(function(){ ???????alert("成功!"); ???}).fail(function(){ ???????alert("失败"); ???}) ???//弹出成功字符串!</script>
进入源码,length为1,remaining计数器为1.
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,//有参数,而且jQuery能够判断为返回promise的function,那么最终计数器为1
//这是主要的deferred ???????????deferred = remaining === 1 ? subordinate : jQuery.Deferred(),//计数器既然为1,那么deferred为参数本身咯。
那么接下来的length>1的if语句就不会进入了。!remaining的if语句也不会进入。这个时候返回
return deferred.promise();//自然是参数所代表的promise
4.when方法的复杂参数的处理
DEMO展示:
<script src="js/jquery-2.0.3.js"></script><script> ???function aaa(){ ???????var dfd=$.Deferred(); ???????dfd.resolve(); ???????return dfd; ???} ???function bbb(){ ???????var dfd=$.Deferred(); ???????dfd.resolve(); ???????return dfd; ???} ???function ccc(){ ???????var dfd=$.Deferred(); ???????dfd.resolve(); ???????return dfd; ???} ???$.when(aaa(),bbb(),ccc()).done(function(){ ???????alert("成功!"); ???}).fail(function(){ ???????alert("失败"); ???}) ???//弹出成功字符串!</script>
针对Demo所示的参数,length为3,remaining算下来就是length长度3.
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,//既然length不等于1,逻辑运算的结果为1,那么||运算符就不用看了//所以结果取length
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),//结果是jQuery.Deferred
if语句就要进入了。
// add listeners to Deferred subordinates; treat others as resolved ???????if ( length > 1 ) { ???????????progressValues = new Array( length ); ???????????progressContexts = new Array( length ); ???????????resolveContexts = new Array( length );//length为多少,创建的数组就为多少//根据length遍历 ???????????for ( ; i < length; i++ ) { ???????????????if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {//遍历如果正确的参数,那么进入if语句//与此同时else是把既有正常的参数,也有基本类型。//那么就把非promise的情况计数器-1 ???????????????????resolveValues[ i ].promise() ???????????????????????.done( updateFunc( i, resolveContexts, resolveValues ) ) ???????????????????????.fail( deferred.reject ) ???????????????????????.progress( updateFunc( i, progressContexts, progressValues ) );//对每个参数进行处理,可以看到done方法是所有的func都要成立//而一旦某个fail了,就会导致master主deferred失败//progress是所有的func的状态变化 ???????????????} else { ???????????????????--remaining; ???????????????} ???????????} ???????}
通过遍历,对每个延迟对象进行处理。
jquery的2.0.3版本源码系列(7):3043行-3183行,deferred延迟对象,对异步的统一管理
原文地址:http://www.cnblogs.com/chenmeng2062/p/7641524.html