jquery.Deferred promise解決異步回調


我們先來看一下編寫AJAX編碼經常遇到的幾個問題:

1.由於AJAX是異步的,所有依賴AJAX返回結果的代碼必需寫在AJAX回調函數中。這就不可避免地形成了嵌套,ajax等異步操作越多,嵌套層次就會越深,代碼可讀性就會越差。

$.ajax({  
    url: url,  
    data: dataObject,  
    success: function(){  
    console.log("I depend on ajax result.");  
    },  
    error: function(){}  
});  
  
console.log("I will print before ajax finished.");  

2.如果AJAX請求之間存在依賴關系,我們的代碼就會形成Pyramid of Doom(金字塔厄運)。比如我們要完成這樣一件事:有4個供Ajax訪問的url地址,需要先Ajax訪問第1個,在第1個訪問完成后,用拿到的返回數據作為參數再訪問第2個,第2個訪問完成后再第3個...以此到4個全部訪問完成。按照這樣的寫法,似乎會變成這樣:

$.ajax({  
    url: url1,  
    success: function(data){  
        $.ajax({  
            url: url2,  
            data: data,  
            success: function(data){  
                $.ajax({  
                    //...  
                });  
            }      
        });  
    }  
});  

3.考慮這種場景,假如我們同時發送兩個Ajax請求,然后要在兩個請求都成功返回后再做一件接下來的事,想一想如果只按前面的方式在各自的調用位置去附加回調,這是不是很困難?

 

可以看到:JavaScript中類似於AJAX這種異步的操作,會導致代碼嵌套層次復雜,可讀性差,有的時候甚至是實現需求都非常困難。為了解決這種異步回調難的問題,CommonJS組織制定了異步模式編程規范Promises/A。目前該規范已經有了很多的實現者,比如Q, when.js, jQuery.Deffered()等。我們以jQuery.Deffered學習下Promise。

 

Promise的狀態

Promise對象有3種可能的狀態:肯定狀態(resolved)、否定狀態(rejected)、等待狀態(pending)。剛開始創建的Promise對象處於pending狀態,只能從pending變成resolved或者是從pending變成rejected狀態。

var df1 = $.Deferred();   
console.log(df1.state());//pending  
  
var df2 = $.Deferred();   
df2.resolve();//resolved  
console.log(df2.state());  
  
var df3 = $.Deferred();   
df3.reject();  
console.log(df3.state());//rejected  

$.Deferred()創建一個延遲對象(也就是Promise對象),deferred.state()可以獲取Promise對象當前所處的狀態。deferred.resolve()和deferred.reject()則是用來改變Promise對象的狀態。

Promise添加回調函數

Promise對象有3種狀態,我們可以分別為這3種狀態注冊回調函數。當Promise處於某個狀態的時候,會觸發這個狀態下注冊的回調函數。

 

var df = $.Deferred();   
df.done(function(){alert("success");});  
df.fail(function(){alert("fail");});  
df.progress(function(){alert("progress");});  
  
df.notify();  
  
df.resolve();  
// df.reject();  

done()、fail()、progress()分別注冊resolved、rejected、pending狀態下的回調函數。通過resolve()、reject()、notify()可以觸發事先注冊的回調函數。

Promise是支持鏈式調用的,上面的代碼可以寫成下面的樣子。

var df = $.Deferred();   
df.done(function(){alert("success");})  
.fail(function(){alert("fail");})  
.progress(function(){alert("progress");});  

Promise支持多個回調函數,會按照注冊順序調用。

var df = $.Deferred();   
df.done(function(){alert("first");})  
.fail(function(){alert("fail");});  
  
df.done(function(){alert("second");});  
df.done(function(){alert("third");});  
  
df.resolve();  

deferred.always()添加的回調函數,無論Promise是resolved狀態還是rejected狀態,都會被調用。

 
var df1 = $.Deferred();   
df1.always(function(type){alert(type);});  
df1.resolve("resolve");  
  
var df2 = $.Deferred();   
df2.always(function(type){alert(type);});  
df2.reject("reject");  

progress()和notify()能夠用來實現進度條效果,因為notify()允許調用多次,而reject()和resolve()只能調用一次。這個很好理解,因為一旦狀態變成resolved或者是rejected,就不能再改變其狀態,也沒有必要。

var df = $.Deferred();     
df.done(function(){alert("success");});    
df.fail(function(){alert("fail");});    
df.progress(function(){alert("progress");});    
    
// resolve()調用2次,但是只能觸發1次success  
df.resolve();    
df.resolve();    
  
var mudf = $.Deferred();     
mudf.done(function(){alert("success");});    
mudf.fail(function(){alert("fail");});    
mudf.progress(function(){alert("progress");});    
    
// 每次調用notify都會觸發progress回調函數  
mudf.notify("%10");    
mudf.notify("%20");    

rejectWith()、resolveWith()、notifyWith()功能上和reject()、resolve()、notify()沒有什么差別,主要差別在於回調函數中的執行上下文(方法中的this)和參數形式。具體差別可以參考"JQuery.Callbacks系列一:api使用

// 老的ajax寫法  
$.ajax({  
  url: "test.html",  
  success: function(){  
    alert("success");  
  },  
  error:function(){  
    alert("error");  
  }  
});  
  
// 使用promise后的寫法  
$.ajax("test.html")  
 .done(function(){})  
 .fail(function(){})  
 .done(function(){)  
 .fail(function(){);  

 

JQuery中的Deferred對象與Promise對象區別

JQuery.Deferred相關的API,有的返回的是Deferred對象,有的返回的是Promise對象。如done()、reject()等大部分函數返回的都是Deferred對象,$.when()和then()函數返回的是Promise對象。具體可以參考JQuery API文檔。

JQuery官方對Promise Objects的解釋是:

This object provides a subset of the methods of the Deferred object (then, done, fail, always, progress, state and promise) to prevent users from changing the state of the Deferred.

可以看到Promise對象其實就是Deferred對象的一部分,Deferred對象提供了notify、reject、resolve等改變狀態的方法,但是Promise對象沒有提供這些方法。

 

文章開始提到的AJAX問題1~3,問題1可以很容易通過Promise得到解決。問題2和問題3是通過$.when()和deferred.then()得到解決,由於這2個API相對來說復雜一些,以后的文章再分析這2個API。

 

詳解"這篇文章中的fire()和fireWith()。

 

上面簡單的介紹了Promise的使用方式,我們可以用Promise的方式來編寫AJAX代碼。可以很容易地看出:使用Promise后代碼嵌套層次少了,代碼是縱向增長的,而不再是橫向增長。而且使用Promise,可以指定多個ajax回調函數。

 
 
jquery Deferred 快速解決異步回調的問題
 
function ok(name){
 
  var dfd = new $.Deferred();
  callback:func(){
 
     return dfd.resolve( response );
  }
 
  return dfd.promise();
}
 
$.when(ok(1),ok(2)).then(function(resp1,resp2){})

//相關API 分成3類

1類:$.when(pro1,pro1) 將多個 promise 對象以and的關系 合並為1個

2類:promise 激發為 解決 deferred.resolve([ args ] ) deferred.resolveWith( context, [ args ] )

和 拒絕 .reject  .rejectWith

context 上下文 替換 this 和通知 .notify  .notifyWith

3類: 對激發的響應  解決時deferred.done(args) 拒絕時 deferred.fail() 通知時 deferred.progress()

不管 解決 或 拒絕 deferred.always()


deferred.then( doneCallbacks, failCallbacks [, progressCallbacks] )


promise(或者叫deferred 延遲對象如何獲取?)

var dfd = new $.Deferred(); return dfd.promise();

 

返回promise當前狀態

deferred.state()  pending(尚未完成) resolved rejected

管道

 

deferred.pipe( [ doneFilter ], [ failFilter ] ) 
 
 
var defer = $.Deferred()
 
var filtered = defer.pipe( null, function( value ) {
 
   return value * 3;
});
 
defer.reject( 6 );
filtered.fail(function( value ) {
   alert( "Value is ( 3*6 = ) 18: " + value );
});

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM