jQuery的deferred物件詳解
-
開發網站的過程中,我們經常遇到某些耗時很長的javascript操作。其中,既有非同步的操作(比如ajax讀取伺服器資料),也有同步的操作(比如遍歷一個大型陣列),它們都不是立即能得到結果的。
-
通常的做法是,為它們指定回撥函式(callback)。即事先規定,一旦它們執行結束,應該呼叫哪些函式。
-
簡單說,
deferred
物件就是jQuery
的回撥函式解決方案。在英語中,defer
的意思是”延遲”,所以deferred
物件的含義就是”延遲”到未來某個點再執行。 -
它解決了如何處理耗時操作的問題,對那些操作提供了更好的控制,以及統一的程式設計介面。它的主要功能,可以歸結為四點
二、ajax操作的鏈式寫法
首先,回顧一下jQuery的ajax操作的傳統寫法:
$.ajax({ url: "test.html", success: function(){ alert("哈哈,成功了!"); }, error:function(){ alert("出錯啦!"); } });
-
在上面的程式碼中,
$.ajax()
接受一個物件引數,這個物件包含兩個方法:success
方法指定操作成功後的回撥函式,error
方法指定操作失敗後的回撥函式。 -
$.ajax()
操作完成後,如果使用的是低於1.5.0
版本的jQuery
,返回的是XHR
物件,你沒法進行鏈式操作;如果高於1.5.0
版本,返回的是deferre
d物件,可以進行鏈式操作。 -
現在,新的寫法是這樣的:
$.ajax("test.html") .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出錯啦!"); });
可以看到,done()相當於success方法,fail()相當於error方法。採用鏈式寫法以後,程式碼的可讀性大大提高
三、指定同一操作的多個回撥函式
-
deferred
物件的一大好處,就是它允許你自由新增多個回撥函式。 -
還是以上面的程式碼為例,如果ajax操作成功後,除了原來的回撥函式,我還想再執行一個回撥函式,怎麼辦?
-
很簡單,直接把它加在後面就行了。
$.ajax("test.html") .done(function(){ alert("哈哈,成功了!");} ) .fail(function(){ alert("出錯啦!"); } ) .done(function(){ alert("第二個回撥函式!");} );
- 回撥函式可以新增任意多個,它們按照新增順序執行
四、為多個操作指定回撥函式
-
deferred
物件的另一大好處,就是它允許你為多個事件指定一個回撥函式,這是傳統寫法做不到的。 -
請看下面的程式碼,它用到了一個新的方法
$.when()
:
$.when($.ajax("test1.html"), $.ajax("test2.html")) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出錯啦!"); });
這段程式碼的意思是,先執行兩個操作$.ajax(“test1.html”)和$.ajax(“test2.html”),如果都成功了,就執行done()指定的回撥函式;如果有一個失敗或都失敗了,就執行fail()指定的回撥函式
五、普通操作的回撥函式介面(上)
-
deferred
物件的最大優點,就是它把這一套回撥函式介面,從ajax操作擴充套件到了所有操作。也就是說,任何一個操作—-不管是ajax操作還是本地操作,也不管是非同步操作還是同步操作—-都可以使用deferred
物件的各種方法,指定回撥函式。 -
我們來看一個具體的例子。假定有一個很耗時的操作
wait
:
var wait = function(){ var tasks = function(){ alert("執行完畢!"); }; setTimeout(tasks,5000); };
-
我們為它指定回撥函式,應該怎麼做呢?
-
很自然的,你會想到,可以使用$.when():
$.when(wait()) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出錯啦!"); });
- 但是,這樣寫的話,done()方法會立即執行,起不到回撥函式的作用。原因在於$.when()的引數只能是deferred物件,所以必須對wait()進行改寫:
var dtd = $.Deferred(); // 新建一個deferred物件 var wait = function(dtd){ var tasks = function(){ alert("執行完畢!"); dtd.resolve(); // 改變deferred物件的執行狀態 }; setTimeout(tasks,5000); return dtd; };
-
現在,
wait()
函式返回的是deferred
物件,這就可以加上鍊式操作了。
$.when(wait(dtd)) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出錯啦!"); });
-
wait()
函式執行完,就會自動執行done()
方法指定的回撥函式。
六、deferred.resolve()方法和deferred.reject()方法
jQuery規定,deferred物件有三種執行狀態—-未完成,已完成和已失敗。如果執行狀態是”已完成”(resolved),deferred物件立刻呼叫done()方法指定的回撥函式;如果執行狀態是”已失敗”,呼叫fail()方法指定的回撥函式;如果執行狀態是”未完成”,則繼續等待,或者呼叫progress()方法指定的回撥函式(jQuery1.7版本新增)
var dtd = $.Deferred(); // 新建一個Deferred物件 var wait = function(dtd){ var tasks = function(){ alert("執行完畢!"); dtd.reject(); // 改變Deferred物件的執行狀態 }; setTimeout(tasks,5000); return dtd; }; $.when(wait(dtd)) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出錯啦!"); });
七、小結:deferred物件的方法
-
(1)
$.Deferred()
生成一個deferred
物件。 -
(2)
deferred.done()
指定操作成功時的回撥函式 -
(3)
deferred.fail()
指定操作失敗時的回撥函式 -
(4)
deferred.promise()
沒有引數時,返回一個新的
deferred
物件,該物件的執行狀態無法被改變;接受引數時,作用為在引數物件上部署deferred
介面。 -
(5)
deferred.resolve()
手動改變deferred
物件的執行狀態為”已完成”,從而立即觸發done()方法。
-
(6)
deferred.reject()
這個方法與deferred.resolve()
正好相反,呼叫後將deferred
物件的執行狀態變為”已失敗”,從而立即觸發fail()
方法。
-
(7)
$.when()
為多個操作指定回撥函式。
除了這些方法以外,deferred
物件還有二個重要方法,上面的教程中沒有涉及到。
-
(8)
deferred.then()
有時為了省事,可以把done()和fail()合在一起寫,這就是then()方法
$.when($.ajax( "/main.php" )) .then(successFunc, failureFunc );
如果then()
有兩個引數,那麼第一個引數是done()
方法的回撥函式,第二個引數是fail()
方法的回撥方法。如果then()
只有一個引數,那麼等同於done()