jQuery使用():Deferred有状态的回调列表(含源码)

  • deferred的功能及其使用
  • deferred的实现原理及模拟源码

 一、deferred的功能及其使用

deferred的底层是基于callbacks实现的,建议再熟悉callbacks的内部机制前提下阅读这篇博客,如果需要了解callbacks可以参考:jQuery使用():Callbacks回调函数列表之异步编程(含源码分析)

  • deferred.done() 向成功状态的回调函数列表中添加回调方法
  • deferred.fail() 向失败状态的回调函数列表中添加回调方法
  • deferred.progress() 向正在进行状态的回调函数列表中添加回调方法
  • deferred.resolve() 触发成功状态的回调函数列表所有方法
  • deferred.reject() 触发失败状态的回调函数列表所有方法
  • deferred.notify() 触发正在进行状态的回调函数列表(当resolve或reject被触发后就不能触发notify)

本质上done、fail、progress实质上分别指向了缓存三种状态的回调对象的add方法,统一由deferred方法引用管理,resolve、reject、notify同理分别指向了三个状态的回调对象的fire方法(内部实现指向fireWith,为了设置this指向deferred或者promise)。下面使用这六个方法来实现一个模拟异步状态回调事件:

 1 var df = $.Deferred();
 2 //注册成功的回调函数
 3 df.done(function(a){
 4     console.log('ho yeah I do it!' + a);
 5 });
 6 //注册失败的回调函数
 7 df.fail(function(a){
 8     console.log('sorry I am loser...' + a);
 9 });
10 //注册进行时的函数
11 df.progress(function(a){
12     console.log("waiting???" + a);
13 });
14 //用定时器模拟一个异步状态回调事件 >60 表示成功 ; < 50表示失败 ; 60><50表示正在进行
15 setInterval(function(){
16     var score = Math.random() * 100;
17     if(score > 60){
18         df.resolve("simpleness");
19     }else if(score < 50){
20         df.reject("difficult");
21     }else{
22         df.notify('be surprised to be dumb');
23     }
24 },1000);

通过deferred对象同时管理三个回调对象,让代码语义化实现的更优雅,同时也降低了代码的冗余。添加回调函数的形式于callback。add方法一致,可以实现多个同时添加。但是deferred做的远远不止这些,由于deferred对象是同时存在添加和执行两种方法,为了保证调用只能在特定位置触发,deferred还实现了promise对象只用来实现注册方法,promis对象上只有指向回调对象add的done、fail、progress方法,以及一个then用来更便捷的添加回调函数的方法。

所以上面的方法可以修改为:

 1 //用定时器模拟一个异步状态回调事件 >60 表示成功 ; < 50表示失败 ; 60><50表示正在进行
 2 function createScore(){
 3     var df = $.Deferred();
 4     setInterval(function(){
 5         var score = Math.random() * 100;
 6         if(score > 60){
 7             df.resolve("simpleness");
 8         }else if(score < 50){
 9             df.reject("difficult");
10         }else{
11             df.notify('be surprised to be dumb');
12         }
13     },1000);
14     return df.promise();
15 }
16 var pom = createScore();
17 //注册成功的回调函数
18 pom.done(function(a){
19     console.log('ho yeah I do it!' + a);
20 });
21 //注册失败的回调函数
22 pom.fail(function(a){
23     console.log('sorry I am loser...' + a);
24 });
25 //注册进行时的函数
26 pom.progress(function(a){
27     console.log("waiting???" + a);
28 });

上面的代码修改后,就是一个盗版的ajax的事件反馈机制,在jQuery.ajax中源码就是通过deferred的异步队列来实现的。前面还有提到then更便捷的回调注册方法又是什么呢?下面来看通过then方法改造上面的代码:

//上面的代码18~28行可以采用这段代码替换
pom.then(function(a){
    console.log('ho yeah I do it!' + a);
},function(a){
    console.log('sorry I am loser...' + a);
},function(a){
    console.log("waiting???" + a);
});

这个模拟示例可以完全采用这两种代码任意一种实现,那这两种代码存在什么区别呢?区别就是then方法可以传入三个参数,分别对应的是done、fail、progress方法的函数注册,但是then不能给同一个状态注册多个回调函数,而done、fail、progress可以像callback.add()那样同时注册多个函数,因为done、fail、progress本身就是指向add()别称。但是then方法还有另一个功能就是能连续注册来替代这种缺陷,并且还可以接收来自上一个方法的返回值作为参数:

 1 pom.then(function(a){
 2     console.log('ho yeah I do it!' + a);
 3     return "oK"
 4 },function(a){
 5     console.log('sorry I am loser...' + a);
 6     return "no"
 7 },function(a){
 8     console.log("waiting???" + a);
 9     return "why"
10 }).then(function(param){
11     console.log(param);//oK
12 },function(param){
13     console.log(param);//no
14 },function(param){
15     console.log(param);//why
16 });

但是需要注意的是,返回值不能是新的deferred对象,如果是一个新的异步延迟对象返回,后面继续使用then方法就是作用在新的异步延迟对象上。

 二、deferred的实现原理及模拟源码

这部分源码是基于jQuery使用():Callbacks回调函数列表之异步编程(含源码分析)的模拟回调函数列表对象实现的,没有测试jQuery的Callbacks对象,代码暂时实现了promise()方法,then()方法没有实现,今天有事,有时间再来添加。

 1 function clone(origin, target){
 2     for(var ele in origin){
 3         target[ele] = origin[ele];
 4     }
 5     return target;
 6 }        
 7 function Deferred(fuc){
 8     //异步延迟对象
 9     var deferred = {}
10     var tuples = [
11         ["resolve","done",Callback("once memory")],
12         ["reject","fail",Callback("once memory")],
13         ["notify","progress",Callback("memory")]
14     ];
15     var statesum = true;
16     //异步延迟对象的注册对象
17     var promise = {
18         //返回deferred的promise注册对象   
19         //源码中有obj的合并采用extend实现,这里写了一个简单的克隆方法
20         promise:function(obj){
21             return obj != null ? clone(promise,obj) : promise;
22         }
23     }
24     var pending = true;
25     for(var tuple in tuples){
26         var list = tuples[tuple][2];
27         //添加deferred的回调函数注册方法 done fail progress
28         promise[tuples[tuple][1]] = list.add;
29         //添加deferred的回调函数触发执行方法 resolve reject ontify
30         deferred[tuples[tuple][0]] = (function(i,obj){
31             return function(){
32                 if(pending){
33                     // console.log(this.state);
34                     deferred[tuples[i][0]+"With"](obj === deferred ? obj : promise,arguments);
35                     tuples[i][0] == "resolve" || tuples[i][0] == "reject" ? pending = false : "";
36                 }
37                 return obj;
38             }
39         })(tuple,this);
40         deferred[tuples[tuple][0] + "With"] = list.fireWith;
41     }
42     //将promise合并到deferred上 -- 同样采用克隆方法实现
43     promise.promise(deferred);
44     //fcn执行 上下文指向deferred  参数设置为deferred
45     if(fuc){
46         fuc.call(deferred,deferred);
47     }
48     return deferred;
49 }

猜你喜欢

转载自www.cnblogs.com/ZheOneAndOnly/p/10549999.html
今日推荐