jQuery之Deferred對象詳解
deferred對象是jQuery對Promises接口的實現(xiàn)。它是非同步操作的通用接口,可以被看作是一個等待完成的任務(wù),開發(fā)者通過一些通過的接口對其進(jìn)行設(shè)置。事實上,它扮演代理人(proxy)的角色,將那些非同步操作包裝成具有某些統(tǒng)一特性的對象,典型例子就是Ajax操作、網(wǎng)頁動畫、web worker等等。
jQuery的所有Ajax操作函數(shù),默認(rèn)返回的就是一個deferred對象。
Promises是什么
由于JavaScript單線程的特點,如果某個操作耗時很長,其他操作就必需排隊等待。為了避免整個程序失去響應(yīng),通常的解決方法是將那些排在后面的操作,寫成“回調(diào)函數(shù)”(callback)的形式。這樣做雖然可以解決問題,但是有一些顯著缺點:
1.回調(diào)函數(shù)往往寫成函數(shù)參數(shù)的形式,導(dǎo)致函數(shù)的輸入和輸出非常混亂,整個程序的可閱讀性差;
2.回調(diào)函數(shù)往往只能指定一個,如果有多個操作,就需要改寫回調(diào)函數(shù)。
3.整個程序的運(yùn)行流程被打亂,除錯和調(diào)試的難度都相應(yīng)增加。
Promises就是為了解決這些問題而提出的,它的主要目的就是取代回調(diào)函數(shù),成為非同步操作的解決方案。它的核心思想就是讓非同步操作返回一個對象,其他操作都針對這個對象來完成。比如,假定ajax操作返回一個Promise對象。
var promise = get('http://www.example.com');
然后,Promise對象有一個then方法,可以用來指定回調(diào)函數(shù)。一旦非同步操作完成,就調(diào)用指定的回調(diào)函數(shù)。
promise.then(function (content) {
console.log(content)
})
可以將上面兩段代碼合并起來,這樣程序的流程看得更清楚。
get('http://www.example.com').then(function (content) {
console.log(content)
})
在1.7版之前,jQuery的Ajax操作采用回調(diào)函數(shù)。
$.ajax({
url:"/echo/json/",
success: function(response)
{
console.info(response.name);
}
});
1.7版之后,Ajax操作直接返回Promise對象,這意味著可以用then方法指定回調(diào)函數(shù)。
$.ajax({
url: "/echo/json/",
}).then(function (response) {
console.info(response.name);
});
deferred對象的方法
$.deferred()方法
作用是生成一個deferred對象。
var deferred = $.deferred();
done() 和 fail()
這兩個方法都用來綁定回調(diào)函數(shù)。done()指定非同步操作成功后的回調(diào)函數(shù),fail()指定失敗后的回調(diào)函數(shù)。
var deferred = $.Deferred();
deferred.done(function(value) {
alert(value);
});
它們返回的是原有的deferred對象,因此可以采用鏈?zhǔn)綄懛?,在后面再鏈接別的方法(包括done和fail在內(nèi))。
resolve() 和 reject()
這兩個方法用來改變deferred對象的狀態(tài)。resolve()將狀態(tài)改為非同步操作成功,reject()改為操作失敗。
var deferred = $.Deferred();
deferred.done(function(value) {
alert(value);
});
deferred.resolve("hello world");
一旦調(diào)用resolve(),就會依次執(zhí)行done()和then()方法指定的回調(diào)函數(shù);一旦調(diào)用reject(),就會依次執(zhí)行fail()和then()方法指定的回調(diào)函數(shù)。
state方法
該方法用來返回deferred對象目前的狀態(tài)。
var deferred = new $.Deferred();
deferred.state(); // "pending"
deferred.resolve();
deferred.state(); // "resolved"
該方法的返回值有三個:
1.pending:表示操作還沒有完成。
2.resolved:表示操作成功。
3.rejected:表示操作失敗。
notify() 和 progress()
progress()用來指定一個回調(diào)函數(shù),當(dāng)調(diào)用notify()方法時,該回調(diào)函數(shù)將執(zhí)行。它的用意是提供一個接口,使得在非同步操作執(zhí)行過程中,可以執(zhí)行某些操作,比如定期返回進(jìn)度條的進(jìn)度。
var userProgress = $.Deferred();
var $profileFields = $("input");
var totalFields = $profileFields.length
userProgress.progress(function (filledFields) {
var pctComplete = (filledFields/totalFields)*100;
$("#progress").html(pctComplete.toFixed(0));
});
userProgress.done(function () {
$("#thanks").html("Thanks for completing your profile!").show();
});
$("input").on("change", function () {
var filledFields = $profileFields.filter("[value!='']").length;
userProgress.notify(filledFields);
if (filledFields == totalFields) {
userProgress.resolve();
}
});
then()
then()的作用也是指定回調(diào)函數(shù),它可以接受三個參數(shù),也就是三個回調(diào)函數(shù)。第一個參數(shù)是resolve時調(diào)用的回調(diào)函數(shù),第二個參數(shù)是reject時調(diào)用的回調(diào)函數(shù),第三個參數(shù)是progress()方法調(diào)用的回調(diào)函數(shù)。
deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
在jQuery 1.8之前,then()只是.done().fail()寫法的語法糖,兩種寫法是等價的。在jQuery 1.8之后,then()返回一個新的deferred對象,而done()返回的是原有的deferred對象。如果then()指定的回調(diào)函數(shù)有返回值,該返回值會作為參數(shù),傳入后面的回調(diào)函數(shù)。
var defer = jQuery.Deferred();
defer.done(function(a,b){
return a * b;
}).done(function( result ) {
console.log("result = " + result);
}).then(function( a, b ) {
return a * b;
}).done(function( result ) {
console.log("result = " + result);
}).then(function( a, b ) {
return a * b;
}).done(function( result ) {
console.log("result = " + result);
});
defer.resolve( 2, 3 );
在jQuery 1.8版本之前,上面代碼的結(jié)果是:
result = 2
result = 2
result = 2
在jQuery 1.8版本之后,返回結(jié)果是
result = 2
result = 6
result = NaN
這一點需要特別引起注意。
$.ajax( url1, { dataType: "json" } )
.then(function( data ) {
return $.ajax( url2, { data: { user: data.userId } } );
}).done(function( data ) {
// 從url2獲取的數(shù)據(jù)
});
上面代碼最后那個done方法,處理的是從url2獲取的數(shù)據(jù),而不是從url1獲取的數(shù)據(jù)。
利用then()會修改返回值這個特性,我們可以在調(diào)用其他回調(diào)函數(shù)之前,對前一步操作返回的值進(jìn)行處理。
var post = $.post("/echo/json/")
.then(function(p){
return p.firstName;
});
post.done(function(r){ console.log(r); });
上面代碼先使用then()方法,從返回的數(shù)據(jù)中取出所需要的字段(firstName),所以后面的操作就可以只處理這個字段了。
有時,Ajax操作返回json字符串里面有一個error屬性,表示發(fā)生錯誤。這個時候,傳統(tǒng)的方法只能是通過done()來判斷是否發(fā)生錯誤。通過then()方法,可以讓deferred對象調(diào)用fail()方法。
var myDeferred = $.post('/echo/json/', {json:JSON.stringify({'error':true})})
.then(function (response) {
if (response.error) {
return $.Deferred().reject(response);
}
return response;
},function () {
return $.Deferred().reject({error:true});
}
);
myDeferred.done(function (response) {
$("#status").html("Success!");
}).fail(function (response) {
$("#status").html("An error occurred");
});
always()
always()也是指定回調(diào)函數(shù),不管是resolve或reject都要調(diào)用。
pipe方法
pipe方法接受一個函數(shù)作為參數(shù),表示在調(diào)用then方法、done方法、fail方法、always方法指定的回調(diào)函數(shù)之前,先運(yùn)行pipe方法指定的回調(diào)函數(shù)。它通常用來對服務(wù)器返回的數(shù)據(jù)做初步處理。
promise對象
大多數(shù)情況下,我們不想讓用戶從外部更改deferred對象的狀態(tài)。這時,你可以在deferred對象的基礎(chǔ)上,返回一個針對它的promise對象。我們可以把后者理解成,promise是deferred的只讀版,或者更通俗地理解成promise是一個對將要完成的任務(wù)的承諾。
你可以通過promise對象,為原始的deferred對象添加回調(diào)函數(shù),查詢它的狀態(tài),但是無法改變它的狀態(tài),也就是說promise對象不允許你調(diào)用resolve和reject方法。
function getPromise(){
return $.Deferred().promise();
}
try{
getPromise().resolve("a");
} catch(err) {
console.log(err);
}
上面的代碼會出錯,顯示TypeError {} 。
jQuery的ajax() 方法返回的就是一個promise對象。此外,Animation類操作也可以使用promise對象。
var promise = $('div.alert').fadeIn().promise();
$.when()方法
$.when()接受多個deferred對象作為參數(shù),當(dāng)它們?nèi)窟\(yùn)行成功后,才調(diào)用resolved狀態(tài)的回調(diào)函數(shù),但只要其中有一個失敗,就調(diào)用rejected狀態(tài)的回調(diào)函數(shù)。它相當(dāng)于將多個非同步操作,合并成一個。
$.when(
$.ajax( "/main.php" ),
$.ajax( "/modules.php" ),
$.ajax( "/lists.php" )
).then(successFunc, failureFunc);
上面代碼表示,要等到三個ajax操作都結(jié)束以后,才執(zhí)行then方法指定的回調(diào)函數(shù)。
when方法里面要執(zhí)行多少個操作,回調(diào)函數(shù)就有多少個參數(shù),對應(yīng)前面每一個操作的返回結(jié)果。
$.when(
$.ajax( "/main.php" ),
$.ajax( "/modules.php" ),
$.ajax( "/lists.php" )
).then(function (resp1, resp2, resp3){
console.log(resp1);
console.log(resp2);
console.log(resp3);
});
上面代碼的回調(diào)函數(shù)有三個參數(shù),resp1、resp2和resp3,依次對應(yīng)前面三個ajax操作的返回結(jié)果。
when方法的另一個作用是,如果它的參數(shù)返回的不是一個Deferred或Promise對象,那么when方法的回調(diào)函數(shù)將 立即運(yùn)行。
$.when({testing: 123}).done(function (x){
console.log(x.testing); // "123"
});
上面代碼中指定的回調(diào)函數(shù),將在when方法后面立即運(yùn)行。
利用這個特點,我們可以寫一個具有緩存效果的異步操作函數(shù)。也就是說,第一次調(diào)用這個函數(shù)的時候,將執(zhí)行異步操作,后面再調(diào)用這個函數(shù),將會返回緩存的結(jié)果。
function maybeAsync( num ) {
var dfd = $.Deferred();
if ( num === 1 ) {
setTimeout(function() {
dfd.resolve( num );
}, 100);
return dfd.promise();
}
return num;
}
$.when(maybeAsync(1)).then(function (resp){
$('#target').append('<p>' + resp + '</p>');
});
$.when(maybeAsync(0)).then(function (resp){
$('#target').append( '<p>' + resp + '</p>');
});
上面代碼表示,如果maybeAsync函數(shù)的參數(shù)為1,則執(zhí)行異步操作,否則立即返回緩存的結(jié)果。
實例
wait方法
我們可以用deferred對象寫一個wait方法,表示等待多少毫秒后再執(zhí)行。
$.wait = function(time) {
return $.Deferred(function(dfd) {
setTimeout(dfd.resolve, time);
});
}
使用方法如下:
$.wait(5000).then(function() {
alert("Hello from the future!");
});
改寫setTimeout方法
在上面的wait方法的基礎(chǔ)上,還可以改寫setTimeout方法,讓其返回一個deferred對象。
function doSomethingLater(fn, time) {
var dfd = $.Deferred();
setTimeout(function() {
dfd.resolve(fn());
}, time || 0);
return dfd.promise();
}
var promise = doSomethingLater(function (){
console.log( '已經(jīng)延遲執(zhí)行' );
}, 100);
自定義操作使用deferred接口
我們可以利用deferred接口,使得任意操作都可以用done()和fail()指定回調(diào)函數(shù)。
Twitter = {
search:function(query) {
var dfr = $.Deferred();
$.ajax({
url:"http://search.twitter.com/search.json",
data:{q:query},
dataType:'jsonp',
success:dfr.resolve
});
return dfr.promise();
}
}
使用方法如下:
Twitter.search('intridea').then(function(data) {
alert(data.results[0].text);
});
deferred對象的另一個優(yōu)勢是可以附加多個回調(diào)函數(shù)。
function doSomething(arg) {
var dfr = $.Deferred();
setTimeout(function() {
dfr.reject("Sorry, something went wrong.");
});
return dfr;
}
doSomething("uh oh").done(function() {
alert("Won't happen, we're erroring here!");
}).fail(function(message) {
alert(message)
});
相關(guān)文章
jQuery序列化form表單數(shù)據(jù)為JSON對象的實現(xiàn)方法
這篇文章主要介紹了jQuery序列化form表單數(shù)據(jù)為JSON對象的實現(xiàn)方法,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2018-09-09jQuery使用hide方法隱藏指定元素class樣式用法實例
這篇文章主要介紹了jQuery使用hide方法隱藏指定元素class樣式,實例分析了jQuery中hide隱藏class樣式的技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-03-03把jquery 的dialog和ztree結(jié)合實現(xiàn)步驟
首先準(zhǔn)備好juqury-ui、ztree 的js文件和css 文件,接下來的步驟祥看本文希望對大家有所幫助2013-08-08jquery Ajax實現(xiàn)Select動態(tài)添加數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了jquery Ajax實現(xiàn)Select動態(tài)添加數(shù)據(jù)的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06jQuery EasyUI 菜單與按鈕之創(chuàng)建簡單的菜單和鏈接按鈕
這篇文章主要介紹了jQuery EasyUI 菜單與按鈕之創(chuàng)建簡單的菜單和鏈接按鈕,需要的朋友可以參考下2015-11-11JQuery獲取與設(shè)置HTML元素的內(nèi)容或文本的實現(xiàn)代碼
使用JQuery可以非常容易地添加、獲取和改變某個HTML元素的內(nèi)容,你會為這種簡便感到非常愉悅2014-06-06