大白話講解Promise(三)搞懂jquery中的Promise

http://www.cnblogs.com/lvdabao/p/jquery-deferred.html @呂大豹html

前兩篇咱們講了ES6中的Promise以及Promise/A+規範,在Promise的知識體系中,jquery固然是必不可少的一環,因此本篇就來說講jquery中的Promise,也就是咱們所知道的Deferred對象。
 
事實上,在此以前網上有不少文章在講jquery Deferred對象了,可是總喜歡把ajax和Deferred混在一塊兒講,容易把人搞混。when、done、promise、success、error、fail、then、resolve、reject、always這麼多方法不能揉在一塊兒講,須要把他們捋一捋,哪些是Deferred對象的方法,哪些是ajax的語法糖,咱們須要心知肚明。
 

先講$.Deferred

jquery用$.Deferred實現了Promise規範,$.Deferred是個什麼玩意呢?仍是老方法,打印出來看看,先有個直觀印象:
var def = $.Deferred();
console.log(def);
輸出以下:
$.Deferred()返回一個對象,咱們能夠稱之爲Deferred對象,上面掛着一些熟悉的方法如:done、fail、then等。jquery就是用這個Deferred對象來註冊異步操做的回調函數,修改並傳遞異步操做的狀態。
 
Deferred對象的基本用法以下,爲了避免與ajax混淆,咱們依舊舉setTimeout的例子:
複製代碼
function runAsync(){
    var def = $.Deferred();
    //作一些異步操做
    setTimeout(function(){
        console.log('執行完成');
        def.resolve('隨便什麼數據');
    }, 2000);
    return def;
}
runAsync().then(function(data){
    console.log(data)
});
複製代碼
在runAsync函數中,咱們首先定義了一個def對象,而後進行一個延時操做,在2秒後調用def.resolve(),最後把def做爲函數的返回。調用runAsync的時候將返回def對象,而後咱們就能夠.then來執行回調函數。
 
是否是感受和ES6的Promise很像呢?咱們來回憶一下第一篇中ES6的例子:
複製代碼
function runAsync(){
    var p = new Promise(function(resolve, reject){
        //作一些異步操做
        setTimeout(function(){
            console.log('執行完成');
            resolve('隨便什麼數據');
        }, 2000);
    });
    return p;           
}
runAsync()
複製代碼
區別在何處一看便知。因爲jquery的def對象自己就有resolve方法,因此咱們在建立def對象的時候並未像ES6這樣傳入了一個函數參數,是空的。在後面能夠直接def.resolve()這樣調用。
 
這樣也有一個弊端,由於執行runAsync()能夠拿到def對象,而def對象上又有resolve方法,那麼豈不是能夠在外部就修改def的狀態了?好比我把上面的代碼修改以下:
var d = runAsync();
d.then(function(data){
    console.log(data)
});
d.resolve('在外部結束');
現象會如何呢?並不會在2秒後輸出「執行完成」,而是直接輸出「在外部結束」。由於咱們在異步操做執行完成以前,沒等他本身resolve,就在外部給resolve了。這顯然是有風險的,好比你定義的一個異步操做並指定好回調函數,有可能被別人給提早結束掉,你的回調函數也就不能執行了。
 
怎麼辦?jquery提供了一個promise方法,就在def對象上,他能夠返回一個受限的Deferred對象,所謂受限就是沒有resolve、reject等方法,沒法從外部來改變他的狀態,用法以下:
複製代碼
function runAsync(){
    var def = $.Deferred();
    //作一些異步操做
    setTimeout(function(){
        console.log('執行完成');
        def.resolve('隨便什麼數據');
    }, 2000);
    return def.promise(); //就在這裏調用
}
複製代碼
這樣返回的對象上就沒有resolve方法了,也就沒法從外部改變他的狀態了。這個promise名字起的有點奇葩,容易讓咱們搞混,其實他就是一個返回受限Deferred對象的方法,與Promise規範沒有任何關係,僅僅是名字叫作promise罷了。雖然名字奇葩,可是推薦使用。
 

then的鏈式調用

既然Deferred也是Promise規範的實現者,那麼其餘特性也必須是支持的。鏈式調用的用法以下:
複製代碼
var d = runAsync();

d.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return runAsync3();
})
.then(function(data){
    console.log(data);
});
複製代碼
與咱們第一篇中的例子基本同樣,能夠參照。
 

done與fail

咱們知道,Promise規範中,then方法接受兩個參數,分別是執行完成和執行失敗的回調,而jquery中進行了加強,還能夠接受第三個參數,就是在pending狀態時的回調,以下:
deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
除此以外,jquery還增長了兩個語法糖方法,done和fail,分別用來指定執行完成和執行失敗的回調,也就是說這段代碼:
d.then(function(){
    console.log('執行完成');
}, function(){
    console.log('執行失敗');
});
與這段代碼是等價的:
複製代碼
d.done(function(){
    console.log('執行完成');
})
.fail(function(){
    console.log('執行失敗');
});
複製代碼
 

always的用法

jquery的Deferred對象上還有一個always方法,不論執行完成仍是執行失敗,always都會執行,有點相似ajax中的complete。不贅述了。
 

$.when的用法

jquery中,還有一個$.when方法來實現Promise,與ES6中的all方法功能同樣,並行執行異步操做,在全部的異步操做執行完後才執行回調函數。不過$.when並無定義在$.Deferred中,看名字就知道,$.when,它是一個單獨的方法。與ES6的all的參數稍有區別,它接受的並非數組,而是多個Deferred對象,以下:
$.when(runAsync(), runAsync2(), runAsync3())
.then(function(data1, data2, data3){
    console.log('所有執行完成');
    console.log(data1, data2, data3);
});
jquery中沒有像ES6中的race方法嗎?就是以跑的快的爲準的那個方法。對的,jquery中沒有。
 
以上就是jquery中Deferred對象的經常使用方法了,還有一些其餘的方法用的也很少,乾脆就不記它了。接下來該說說ajax了。
 

ajax與Deferred的關係

jquery的ajax返回一個受限的Deferred對象,還記得受限的Deferred對象吧,也就是沒有resolve方法和reject方法,不能從外部改變狀態。想一想也是,你發一個ajax請求,別人從其餘地方給你取消掉了,也是受不了的。
 
既然是Deferred對象,那麼咱們上面講到的全部特性,ajax也都是能夠用的。好比鏈式調用,連續發送多個請求:
複製代碼
req1 = function(){
    return $.ajax(/*...*/);
}
req2 = function(){
    return $.ajax(/*...*/);
}
req3 = function(){
    return $.ajax(/*...*/);
}

req1().then(req2).then(req3).done(function(){
    console.log('請求發送完畢');
});
複製代碼
明白了ajax返回對象的實質,那咱們用起來就駕輕就熟了。
 

success、error與complete

這三個方法或許是咱們用的最多的,使用起來是這樣的:
$.ajax(/*...*/)
.success(function(){/*...*/})
.error(function(){/*...*/})
.complete(function(){/*...*/})
分別表示ajax請求成功、失敗、結束的回調。這三個方法與Deferred又是什麼關係呢?其實就是語法糖,success對應done,error對應fail,complete對應always,就這樣,只是爲了與ajax的參數名字上保持一致而已,更方便你們記憶,看一眼源碼:
deferred.promise( jqXHR ).complete = completeDeferred.add;
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;
complete那一行那麼寫,是爲了減小重複代碼,其實就是把done和fail又調用一次,與always中的代碼同樣。deferred.promise( jqXHR )這句也能看出,ajax返回的是受限的Deferred對象。
 
jquery加了這麼些個語法糖,雖然上手門檻更低了,可是卻形成了必定程度的混淆。一些人雖然這麼寫了好久,卻一直不知道其中的原理,在面試的時候只能答出一些皮毛,這是很很差的。這也是我寫這篇文章的原因。
 
jquery中Deferred對象涉及到的方法不少,本文儘可能分門別類的來介紹,但願能幫你們理清思路。總結一下就是:$.Deferred實現了Promise規範,then、done、fail、always是Deferred對象的方法。$.when是一個全局的方法,用來並行運行多個異步任務,與ES6的all是一個功能。ajax返回一個Deferred對象,success、error、complete是ajax提供的語法糖,功能與Deferred對象的done、fail、always一致。就醬。
相關文章
相關標籤/搜索