參考文章:
jQuery的deferred對象詳解以及我寫過的Promise文章
jQuery.defered最新的文檔:http://www.css88.com/jqapi-1.9/category/deferred-object/
前言
jQuery跟人的第一印象就是操作DOM的庫缺厉,新手學習jQuery也是先從操作DOM學起,我當年也是如此。學會了操作DOM之后再學學事件和Ajax蝶防,就以為學成了缤灵,然而jQuery.defered對象由于用處不大妒御,我始終沒有系統(tǒng)的學習佩厚,只是在jQuery.ajax里面間接使用過蚊惯。到今天辆床,雖然ES6和Vue等新技術很有市場佳晶,但是jQuery依然是小型項目的最佳解決方案,更何況我們開發(fā)PC頁面依然必須支持IE8讼载,我的項目也越寫越復雜轿秧,所以我認為jQuery.defered對象是IE8中解決回調問題的優(yōu)選之一。另一套技術方案就是用Promise的polyfill咨堤,它的優(yōu)點是跟原生Promise的語法完全一致菇篡,缺點是要專門引入庫。
本文只對比兩種技術方案的區(qū)別一喘,適合于已經熟悉原生Promise理念驱还,也研究過jQuery.defered對象的人閱讀。
鏈式操作舉例
找出我從前的一個例子(原文)凸克,隔一段時間打印一些字符议蟆。原生寫法如下:
var promise = new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('第一個回調');
resolve(3);
}, 3000);
});
promise.then(function(value) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('第二個回調');
console.log(value * 2);
resolve(value * 2);
}, 2000);
});
}).then(function(value) {
setTimeout(function() {
console.log('第三個回調');
console.log(value * 2);
}, 1000);
});
也就是先new一個Promise對象,然后在這個Promise對象身上加then方法萎战。
jQuery.defered()方案寫法如下:
var dfd = function() {
var deferred = $.Deferred();
setTimeout(function() {
console.log("執(zhí)行完畢1");
deferred.resolve(1);
},4000);
return deferred;
};
$.when(dfd())
.then(function(value){
var deferred = $.Deferred();
setTimeout(function() {
console.log("執(zhí)行完畢2");
console.log('value = ' + value);
deferred.resolve(value + 1);
},3000);
return deferred;
})
.then(function(value){
var deferred = $.Deferred();
setTimeout(function() {
console.log("執(zhí)行完畢3");
console.log('value = ' + value);
deferred.resolve(value + 1);
},2000);
return deferred;
});
首先研究一下$.when()(文檔)
這個方法的正確使用姿勢是:傳入0個或者多個延遲對象咐容。當全部成功,則執(zhí)行.done蚂维,如果有一個失敗疟丙,就執(zhí)行.fail颖侄。
在多個延遲對象傳遞給jQuery.when() 的情況下,.when()根據一個新的“宿主” Deferred(延遲)對象享郊,跟蹤所有已通過Deferreds聚集狀態(tài)览祖,返回一個Promise對象。
當所有的延遲對象被解決(resolve)時炊琉,“宿主” Deferred(延遲)對象才會解決(resolved)該方法展蒂,或者當其中有一個延遲對象被拒絕(rejected)時,“宿主” Deferred(延遲)對象就會reject(拒絕)該方法苔咪。
如果“宿主” Deferred(延遲)對象是解決(resolved)狀態(tài)時锰悼, “宿主” Deferred(延遲)對象的 doneCallbacks (解決回調)將被執(zhí)行。參數傳遞給 doneCallbacks提供這解決(resolved)值給每個對應的Deferreds對象团赏,并匹配Deferreds傳遞給 jQuery.when()的順序箕般。
再看看構造延遲對象的區(qū)別
從書寫上,兩個例子略有區(qū)別舔清,jq的寫法是以普通函數return延遲對象丝里,原生寫法是用構造函數直接構造延遲對象。那么jq有沒有直接構造延遲對象的辦法体谒?有杯聚。
下面就是以構造函數$.Deferred()
直接構造延遲對象的例子,構造函數會給回調函數的參數傳入一個空延遲對象抒痒,所以內部不需要var deferred = $.Deferred();
幌绍。then里的代碼完全相同。
$.Deferred(function(dfd){
setTimeout(function(){
console.log("1執(zhí)行完畢故响!");
dfd.resolve(1);
},4000);
return dfd;
})
.then(function(value){
var deferred = $.Deferred();
setTimeout(function(){
console.log("2執(zhí)行完畢傀广!");
deferred.resolve(value + 1);
},3000);
return deferred;
})
.then(function(value){
var deferred = $.Deferred();
setTimeout(function(){
console.log("3執(zhí)行完畢!" + value);
deferred.resolve();
},2000);
return deferred;
});
這兩種的代碼量彩届、理解難易度都差不多主儡,如果你的“第一步操作”只需要創(chuàng)建一個延遲對象,那么用誰都可以惨缆,如果你的“第一步操作”需要創(chuàng)建多個延遲對象糜值,而且這些延遲操作是并發(fā)關系,那么只能用$.when()
坯墨,因為它可以接受多個延遲對象寂汇,且會更待所有的操作完成再做出響應。
jQuery.defered對象和原生Promise對象對應關系總結
以下左為jQuery.defered對象捣染,右為原生Promise對象
$.Deferred()類似于new Promise()
deferred.then沒有對應任何原生Promise方法骄瓣,因為deferred.then可以有三個處理程序,第一個是成功后的耍攘,第二個是失敗后的榕栏,第三個是[可選]當Deferred(延遲)對象生成進度通知時被調用的一個函數畔勤。也就是說,deferred.then = deferred.done + deferred.fail + progressFilter扒磁。
deferred.then是jQuery中唯一可以傳遞延遲狀態(tài)的方法庆揪。其實在1.8版本之前,沒有任何方法可以傳遞延遲狀態(tài)妨托,只不過1.8版本開始缸榛,jQ的開發(fā)者們給then賦予了傳遞延遲狀態(tài)的能力,這時候兰伤,很多使用者誤以為done和fail等也可以傳遞延遲狀態(tài)内颗,其實并不是,記住敦腔,直至本文完稿為止均澳,只有then方法有這個能力。
再具體說符衔,就是then方法后面可以接done找前、fail等方法,then可以把延遲狀態(tài)傳遞給done和fail等方法柏腻,但是纸厉,done系吭、fail方法后面不可以接then方法五嫂,說白了done、fail就是延遲鏈的終點肯尺,不會再有延遲對象傳遞沃缘。這跟原生Promises是不一樣的。
deferred.done也沒有對應任何原生Promise方法则吟,千萬不要以為deferred.done對應單參數的new Promise().then槐臀,因為deferred.done連綴書寫的話,各個回調函數是并列關系氓仲,回調函數里面如果有異步任務水慨,會導致執(zhí)行順序不符合你的期待。
deferred.fail跟deferred.done同理敬扛,不要以為deferred.fail對應new Promise().catch晰洒。而且,new Promise().catch后面可以繼續(xù)傳遞延遲狀態(tài)啥箭,但是deferred.fail不可以谍珊。
看了上文,你就也可以知道急侥,deferred.catch也不對應new Promise().catch砌滞。deferred.catch是deferred.then( null, fn )的別名侮邀,deferred.catch跟deferred.fail的區(qū)別在于deferred.fail更強大,可以接受多個函數贝润。
deferred.always也沒有對應方法绊茧,因為always可以傳入處理程序列表,但是這些處理程序是同類并列關系题暖,并不是說前一個函數對應成功按傅,后一個函數對應失敗。所以jQuery官方也建議胧卤,不要去用if判斷上一步到底是成功還是失敗唯绍,而是應當執(zhí)行一些無狀態(tài)的語句,這樣才符合always單詞的語義枝誊。如果想判斷诚纸,依然推薦你用done和fail翁逞。
$.when()對應Promise.all()
沒有方法對應Promise.race()
剩余其他deferred方法用得少,暫不舉例
現(xiàn)在就可以看出來各方的優(yōu)勢了,原生方法的優(yōu)勢是符合業(yè)界標準缚俏,理解簡單,jQ的優(yōu)勢是除了沒有Promise.race()耀石,其他方法很多力崇。所以,技術選型時:
如果你確定不需要Promise.race()功能古瓤,但很需要jQ自帶的一些方法全部方法看這里止剖,那么,選jQuery.defered落君。
如果你很需要Promise.race()功能穿香,并不需要jQ自帶的一系列方法,那么用Promise的polyfill绎速,或原生Promise即可皮获。