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