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 }?
轉載于:https://www.cnblogs.com/ZheOneAndOnly/p/10549999.html
總結
以上是生活随笔為你收集整理的jQuery使用():Deferred有状态的回调列表(含源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringBoot项目部署到独立的To
- 下一篇: 【转】为了修复打码女神脸,他们提出二阶段