目录

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 id="btn">按钮</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.add done、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[ ],
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 );
}

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;
}
}
}

通过遍历,对每个延迟对象进行处理。

最新文章

  1. Mac利用PD虚拟机安装Centos7
  2. Hexo - 快速,轻量,强大的 Node.js 博客框架
  3. ubuntu 安装Firefox 29.0
  4. js求和算法研究
  5. Uva 10129 - Play on Words 单词接龙 欧拉道路应用
  6. python之方法总结
  7. width:auto; 和 width:100%;的不同
  8. nettyclient异步获取数据
  9. JavaScript数组去重方法汇总
  10. 【转】Linux中的特殊权限粘滞位(sticky bit)详解
  11. 【Linux】如何在Linux上安装使用SSH
  12. Oracle 查询表空间使用情况
  13. 二叉搜索树的最近公共祖先的golang实现
  14. siege 高并发测试工具
  15. DAY27.XIA.面向對象
  16. javascript继承之组合继承(三)
  17. Java字符串拼接效率对比
  18. Shell 学习笔记之函数
  19. e676. 把彩色图像转换为灰色
  20. ZT Android4.2关于bluetooth在HAL层的分析(1)

热门文章

  1. [01] Servlet是什么
  2. webservice03#schema#元素属性定义
  3. 深入理解分布式调度框架TBSchedule及源码分析
  4. SIT 和 UAT
  5. Axios源码阅读笔记#1 默认配置项
  6. Opencv的使用,NDK的简单使用
  7. pytorch实现VAE
  8. EXIT_SUCCESS EXIT_FAILURE
  9. 实例说明MVC,MVP,MVVM架构
  10. http://codeforces.com/problemset/problem/545/D