文章出處
文章列表
前兩篇我們講了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);
輸出如下:
![](https://imageproxy.pixnet.cc/imgproxy?url=https://images2015.cnblogs.com/blog/520134/201603/520134-20160329213911379-199799420.png)
$.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一致。就醬。
文章列表
全站熱搜