jQuery的promise與deferred對(duì)象在異步回調(diào)中的作用
一、前言
為了讓前端們從回調(diào)的地獄中回到天堂, jQuery 也引入了 Promise 的概念。 Promise 是一種令代碼異步行為更加優(yōu)雅的抽象,有了它,我們就可以像寫(xiě)同步代碼一樣去寫(xiě)異步代碼。 jQuery 從1.5版本開(kāi)始實(shí)現(xiàn)了 CommonJS Promise/A 規(guī)范這一重量級(jí)方案,不過(guò)沒(méi)有嚴(yán)格按照規(guī)范進(jìn)行實(shí)現(xiàn),有一些API上的差異。
好,讓我們來(lái)看看他們的特性吧( 本文示例基于jquery 1.8版本以上 )。
二、示例
以前寫(xiě)動(dòng)畫(huà)時(shí),我們通常是這么干的:
$('.animateEle').animate({ opacity:'.5' }, 4000,function(){ $('.animateEle2').animate({ width:'100px' },2000,function(){ // 這樣太傷了 $('.animateEle3').animate({ height:'0' },2000); }); });
假如這么使用回調(diào)的話,那就太傷了。幸好,還有一些現(xiàn)成的 Promise 解決方案來(lái)優(yōu)雅地解決這種問(wèn)題。
我們看看 jQuery 提供的解決辦法。
var animate1 = function() { return $('.animateEle1').animate({opacity:'.5'},4000).promise(); }; var animate2 = function() { return $('.animateEle2').animate({width:'100px'},2000).promise(); }; var animate3 = function(){ return $('.animateEle3').animate({height:'0'},2000).promise(); }; // so easy,有木有,so clear,有木有 $.when(animate1()).then(animate2).then(animate3);
很明顯,更改后的代碼更易懂易讀了。
但是,上面的代碼,有些細(xì)節(jié)的東西并沒(méi)有透露,一不小心,就容易出錯(cuò),得不到我們想要的順序完成動(dòng)畫(huà)的效果。下面讓我們來(lái)全面理解 jQuery 提供的 promise 和 deferred 對(duì)象的方法,看看到底如何使用。
三、promise和deffered對(duì)象方法
promise 對(duì)象其實(shí)就是 deferred 對(duì)象的特例,因?yàn)?promise 對(duì)象不能更改異步狀態(tài),而 deferred 對(duì)象可以。這點(diǎn)在他們的方法設(shè)計(jì)上,有著明顯的體現(xiàn)。
1.promise對(duì)象方法
通常,對(duì)于DOM,動(dòng)畫(huà),ajax相關(guān)方法,我們都可以使用 promise 方法。調(diào)用 promise 方法,返回的是 promise 對(duì)象??梢枣?zhǔn)秸{(diào)用 promise 方法。
promise對(duì)象常見(jiàn)的方法有三個(gè) : done , fail , then 。
其它的方法就不要去記了, jquery 這里的接口方法太多了,在我看來(lái)挺啰嗦的,就跟早期的事件方法綁定一樣, live , delegate , bind ,最終不是都?xì)w為 on 來(lái)管了么。
代碼示例,如下:
(1)DOM使用 promise 方法:
var box=$('#box'); box.promise().done(function(ele){ console.log(ele);//jQuery box });
(2)Ajax使用 promise 方法(默認(rèn)返回一個(gè) promise 對(duì)象,所以可以不必顯式調(diào)用 promise 方法):
$.post('/',{}).done(function(data){ console.log('請(qǐng)求成功'); }).fail(function(){ console.log('請(qǐng)求錯(cuò)誤'); });
動(dòng)畫(huà)示例已有,就不重復(fù)列出了。
2.deferred對(duì)象方法
那么Deferred和Promise之間有什么區(qū)別呢?正如你在前面看到的,一個(gè)promise就是一個(gè)由異步函數(shù)返回的對(duì)象。當(dāng)你想要自己編寫(xiě)一個(gè)這樣的函數(shù)時(shí)你需要使用一個(gè)deferred。
一個(gè)deferred對(duì)象能做的和一個(gè)promise對(duì)象差不多,但是它有兩個(gè)函數(shù)來(lái)觸發(fā)done()和fail()函數(shù)。
一個(gè)deferred對(duì)象擁有一個(gè)resolve()函數(shù)來(lái)處理一個(gè)成功的結(jié)果并執(zhí)行與done()相關(guān)的函數(shù)。reject()函數(shù)則用來(lái)處理失敗的結(jié)果并執(zhí)行與fail()相關(guān)的函數(shù)。
你可以給resolve()和reject()函數(shù)都提供參數(shù),然后它們都將傳遞給與done()和fail()相關(guān)的回調(diào)函數(shù)。
promise對(duì)象沒(méi)有resolve()和reject()函數(shù)。這是因?yàn)槟銓romise放到了其他的腳本中并且你也不想promise去resolve或者reject一個(gè)promise。
下面是一個(gè)關(guān)于deferred的簡(jiǎn)單例子。html僅僅是一個(gè)簡(jiǎn)單的擁有id屬性為”result”的空div。
$('#result').html('waiting...'); var promise = wait(); promise.done(result); function result() { $('#result').html('done'); } function wait() { var deferred = $.Deferred(); setTimeout(function() { deferred.resolve(); }, 2000); return deferred.promise(); }
其中,wait()函數(shù)返回了一個(gè)promise。它將在2s之后被解析。除了setTimeout之外,異步函數(shù)中所有的東西都能這樣使用,比如 動(dòng)畫(huà),Web worker等等。wait()函數(shù)中的代碼應(yīng)該很清晰,我們使用了deferred對(duì)象,但是我們返回了一個(gè)限制的promise對(duì)象。
對(duì)于 deferred 對(duì)象呢,也就是使用 $.Deferred() 方法,以及 $.when() 等方法創(chuàng)造出來(lái)的對(duì)象,有如下的常用方法:
resolve , reject , notify ; done , fail , progress ;
另外還有 promise 、 then 和 always 方法。
之所以這么排版,是因?yàn)樗麄兪菍?duì)應(yīng)的,也就是說(shuō): resolve 方法會(huì)觸發(fā) done 的回調(diào)執(zhí)行, reject 會(huì)觸發(fā) fail 的回調(diào), notify 會(huì)觸發(fā) progress 的回調(diào)。
直接看代碼:
var wait = function(ms) { var dtd = $.Deferred(); setTimeout(dtd.resolve, ms); // setTimeout(dtd.reject, ms); // setTimeout(dtd.notify, ms); return dtd.promise(); //此處也可以直接返回dtd }; wait(2500).done(function() { console.log('haha,師太,你可讓老衲久等了'); }).fail(function() { console.log('失敗了'); }).progress(function(res) { console.log('等待中...'); });
我們看到了,上面的代碼中,在 wait 函數(shù)中,返回的是個(gè) promise 對(duì)象,而不是 deferred 對(duì)象。
要知道, promise 對(duì)象是沒(méi)有 resolve , reject , notify 等方法的,也就意味著,你無(wú)法針對(duì) promise 對(duì)象進(jìn)行狀態(tài)更改,只能在 done 或 fail 中進(jìn)行回調(diào)配置。所以,你如果這么調(diào)用 wait(2500).resolve() 將會(huì)報(bào)錯(cuò),因?yàn)?wait(2500) 返回的是個(gè) promise 對(duì)象,不存在 resolve 方法。
但是,這么做,有個(gè)好處,我們把 dtd 這個(gè) deferred 對(duì)象放在了 wai t函數(shù)中,作為了局部變量,避免了全局的污染;進(jìn)一步通過(guò) promise 方法,轉(zhuǎn)化 dtd 這個(gè) deferred 對(duì)象為 promise 對(duì)象,避免了函數(shù) wait 外部可能發(fā)生的狀態(tài)更改(假如我們確實(shí)有這個(gè)需求)。
比如:
var wait = function(ms) { var dtd = $.Deferred(); setTimeout(dtd.resolve, ms); // setTimeout(dtd.reject, ms); // setTimeout(dtd.notify, ms); return dtd; //此處也可以直接返回dtd }; wait(2500).reject().fail(function(){ console.log('失敗了...............'); });
我們?cè)谕獠扛牧?wait 返回的 deferred 對(duì)象的狀態(tài),這樣必然觸發(fā)該對(duì)象的 fail 回調(diào)函數(shù)。
對(duì)于 always 方法,從字面意思上就很容易理解, deferred 對(duì)象無(wú)論是 resolve 還是 reject ,都會(huì)觸發(fā)該方法的回調(diào)。
3.其它共性
此處講講 then 和 $.when 方法的使用。它們對(duì) promise 對(duì)象也適用。
$.when 方法接受多個(gè) deferred 對(duì)象或者純javascript對(duì)象,返回 promise 對(duì)象。
then 方法依次接受三個(gè)回調(diào),分別為 deferred 對(duì)象 resolve , reject , notify 后觸發(fā)的回調(diào),返回一個(gè) promise 對(duì)象。注意,必須傳入函數(shù),而該函數(shù)只有返回一個(gè) promise 對(duì)象,才能夠讓異步事件按照預(yù)期順序來(lái)執(zhí)行。
我們來(lái)看看最開(kāi)始的動(dòng)畫(huà)示例代碼, $.when(animate1()).then(animate2).then(animate3) , $.when 方法中接受了一個(gè) animate1 的函數(shù)執(zhí)行結(jié)果,也就是得到了一個(gè) promise 對(duì)象,而后的 then 中,則只是接受了一個(gè)變量名,這樣得到的結(jié)果是一個(gè)匿名的函數(shù)體,而該函數(shù)中返回的是 promise 對(duì)象。正好符合了我們對(duì) then 接受參數(shù)的要求。
假如我們把執(zhí)行語(yǔ)句改成: $.when(animate1()).then(animate2()).then(animate3()) ,這樣造成的結(jié)果就是三個(gè)動(dòng)畫(huà)同步執(zhí)行了。與 $.when(animate1(),animate2(),animate3()) 無(wú)異。
既然 then 是如此要求,那么與 then 方法類(lèi)似的 done , fail , progress 也是一樣的。
相關(guān)文章
jQuery Datatables表頭不對(duì)齊的解決辦法
這篇文章主要為大家詳細(xì)介紹了jQuery Datatables表頭不對(duì)齊的解決辦法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11jQuery滾動(dòng)加載圖片實(shí)現(xiàn)原理
這篇文章主要介紹了jQuery滾動(dòng)加載圖片實(shí)現(xiàn)原理,通過(guò)四個(gè)方面來(lái)說(shuō)明懶加載技術(shù)的原理,感興趣的小伙伴們可以參考一下2015-12-12javascript loadScript異步加載腳本示例講解
本文講解了javascript異步加載腳本并觸發(fā)回調(diào)函數(shù)的方法,在加載遠(yuǎn)程數(shù)據(jù)的時(shí)候可以用到,下面提供代碼示例和源碼2013-11-11多個(gè)datatable共存造成多個(gè)表格的checkbox都被選中
所以當(dāng)有多個(gè)datatable 引用到一個(gè)頁(yè)面中的時(shí)候,全選事件會(huì)匹配全部的datatable,所以造成全部多個(gè)表格的checkbox被都被選中2013-07-07分享一個(gè)自己動(dòng)手寫(xiě)的jQuery分頁(yè)插件
本文主要是將自己動(dòng)手些jquery分頁(yè)插件的思路和步驟分享給大家,本分頁(yè)插件功能很簡(jiǎn)單,但是卻很實(shí)用,不想其他插件似的,功能一大堆。好了,廢話不多說(shuō),還是看正文吧2014-08-08使用jQuery,Angular實(shí)現(xiàn)登錄界面驗(yàn)證碼詳解
這篇文章主要介紹了使用jQuery,Angular實(shí)現(xiàn)登錄界面驗(yàn)證碼詳解,需要的朋友可以參考下2017-04-04jQuery插件jFade實(shí)現(xiàn)鼠標(biāo)經(jīng)過(guò)的圖片高亮其它變暗
本文給大家介紹的是一款重點(diǎn)突出的jQuery特效插件效果,使用jFade實(shí)現(xiàn)鼠標(biāo)經(jīng)過(guò)的圖片高亮其它變暗,非常實(shí)用,推薦給小伙伴們參考下。2015-03-03iframe中使用jquery進(jìn)行查找的方法【案例分析】
這篇文章主要介紹了iframe中使用jquery進(jìn)行查找的方法,結(jié)合實(shí)際案例形式較為詳細(xì)的分析了jQuery結(jié)合iframe查找的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06JS中Array數(shù)組學(xué)習(xí)總結(jié)
本文主要介紹了JS中Array數(shù)組的相關(guān)知識(shí)。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-01-01