- when方法的语法及使用
- when源码实现分析
一、when方法的语法及使用
1.语法:
$.when( deferreds )
when本身相当于一个延迟回调对象集合的监听,当监听到所有回调对象都被触发了受理回调,它自身的一个延迟回调对象就会触发受理;反之,当它监听的回调对象中有一个触发了拒绝回调,它自身的延迟回调对象就会触发拒绝回调。
如果when方法不传入回调对象和任何实参,when默认为受理,直接触发它自身的受理回调。
所以,如果只单纯的看when自己的API语法是无法使用它的,应该结合$.Deferred.then方法一起使用,严格来说使用when方法时应该是按照以下模式来使用:
1 $.when( deferreds )
2 .then(doneFire,failFire);
关于when的使用就不太多赘述,毕竟使用起来比较简单,下面这个示例可以完整的了解when的使用:
1 function conso(text){
2 console.log(text);
3 }
4 $.when(
5 a().then(conso,conso,conso),
6 b().then(conso,conso,conso),
7 c().then(conso,conso,conso)
8 )
9 .then(function(){
10 console.log("三个异步回调全部触发受理:resolve");
11 }, function(){
12 console.log("三个异步回调没有全部触发受理:reject");
13 });
14
15
16 function a(){
17 var df = $.Deferred();
18 setInterval(function(){
19 var score = Math.random() * 100;
20 if(score > 30){
21 df.resolve("a受理");
22 }else if(score < 20){
23 df.reject("a拒绝");
24 }else{
25 df.notify('a正在进行');
26 }
27 },1000);
28 return df.promise();
29 }
30 function b(){
31 var df = $.Deferred();
32 setInterval(function(){
33 var score = Math.random() * 100;
34 if(score > 30){
35 df.resolve("b受理");
36 }else if(score < 20){
37 df.reject("b拒绝");
38 }else{
39 df.notify('b正在进行');
40 }
41 },1000);
42 return df.promise();
43 }
44 function c(){
45 var df = $.Deferred();
46 setInterval(function(){
47 var score = Math.random() * 100;
48 if(score > 30){
49 df.resolve("c受理");
50 }else if(score < 20){
51 df.reject("c拒绝");
52 }else{
53 df.notify('c正在进行');
54 }
55 },1000);
56 return df.promise();
57 }
二、when源码实现分析
在了解when的源码之前必须先了解$.Callbacks和$.Deferred以及$.Deferred.then的源码实现
相对于Callbacks和Deferred的实现,when的实现就要简单的多,实现方式非常类似Deferred.then的实现模式,Deferred.then是将后一个then节点的触发执行方法添加到前一个Deferred延迟回调列表上,when也是按照这样的实现方式实现的。
但是jQuery中的when实现还是有些疑问,当被监听的一个延迟回调对象受理是第一个触发,如果后面两个触发都是进行中的话,when自身的回调对象无法正确获取参数(undefined);如果第一个延迟回调对象进行中被触发,后面的进行中获得参数都是第一个延迟回调对象的参数。(带我解开这个疑惑以后来在补充模拟源码)
关于js的异步回调管理,我会在ES6的Promise中还会根据其API做详细分析,如果有时间也会试着模拟实现兼容API,在Promise中也同样存在着when方法,我尽量抽些时间模拟实现Promise的when方法作为补充。
源码我读了几遍,还是有些不理解为什么要写那么复杂,我自己写了一个简单的,功能目前我的理解来说与官方文档中所描述的没有差别:
1 when:function(subordinate){
2 let i = 0,
3 resolveValues = [].slice.call(arguments), //将传入的Deferred对象复制一份添加到一个数组中
4 length = resolveValues.length,
5 remaining = length !== 1 || (subordinate && typeof subordinate.promise == 'function') ? length : 0, //记录未处理完的deferred个数
6 deferred = jQuery.Deferred(),
7
8 //更新deferred处理的进度
9 updateResolve = function(value){
10 if(remaining > 1){
11 remaining --;
12 }else if(remaining === 1){
13 deferred.resolveWith(this,value);
14 }
15 },
16 updataReject = function(value){
17 if(remaining){
18 remaining = 0;
19 deferred.rejectWith(this,value);
20 }
21 }
22
23 if(length > 0){
24 for(;i < length; i++){
25 resolveValues[i].done(updateResolve)
26 .fail(updataReject);
27 }
28 }
29 return deferred.promise();
30 }