摘要:給普通的操作指定回調(diào)函數(shù)對象的最大優(yōu)點,就是它把這一套回調(diào)函數(shù)接口,從操作擴展到了所有操作。方法用于指定對象狀態(tài)為已失敗時的回調(diào)函數(shù)。執(zhí)行完畢執(zhí)行成功執(zhí)行失敗接收一個或多個對象作為參數(shù),為其指定回調(diào)函數(shù)。
什么是deferred對象
開發(fā)網(wǎng)站的過程中,我們經(jīng)常遇到某些耗時很長的javascript操作。其中,既有異步的操作(比如ajax讀取服務器數(shù)據(jù)),也有同步的操作(比如遍歷一個大型數(shù)組),它們都不是立即能得到結(jié)果的。
通常的做法是,為它們指定回調(diào)函數(shù)(callback)。即事先規(guī)定,一旦它們運行結(jié)束,應該調(diào)用哪些函數(shù)。
簡單說,deferred對象就是jQuery的回調(diào)函數(shù)解決方案。在英語中,defer的意思是"延遲",所以deferred對象的含義就是"延遲"到未來某個點再執(zhí)行。
它解決了如何處理耗時操作的問題,對那些操作提供了更好的控制,以及統(tǒng)一的編程接口。它的主要功能,可以歸結(jié)為四點。
ajax的鏈式寫法我們來看一個$.ajax請求的例子:
$.ajax({ url: "test.jspx", success: function(data) { // 請求成功了 // TODO }, error: function(error) { // 請求失敗了 // TODO } });
上面的代碼再常見不過了,不過下面的寫法你也一定看過:
$.ajax("test.jspx") .done(function(data) { // 請求成功了 // TODO }) .fail(function(error) { // 請求失敗了 // TODO }); $.ajax({ url: "test.jspx", type:"POST", data: data }).done(function(data) { // 請求成功了 // TODO }).fail(function(error) { // 請求失敗了 // TODO });
其實在1.5.0版本的之前的jquery是不支持鏈式寫法的,只能使用第一種寫法,原因是此版本之前的jquery的ajax操作返回的是一個XHR (XMLHttpRequest) 對象,這個對象沒有像done和fail這樣的回調(diào)方法。
之后的版本返回的是一個deferred對象用promise方法包裝過的對象,可以進行鏈式操作,使用鏈式寫法后,代碼可讀性大大提高。
為一個操作指定多個回調(diào)函數(shù)如果要在一個ajax請求成功后再執(zhí)行別的回調(diào)函數(shù),該怎么辦呢? 直接在加在后面就可以了:
$.ajax("test.jspx") .done(function(data) { // 請求成功了 // TODO }).fail(function(error) { // 請求失敗了 // TODO }).done(function(data) { // 請求成功了 // then TODO });
這種寫法可以支持無數(shù)個回調(diào)函數(shù),這寫回調(diào)函數(shù)將按照添加順序依次執(zhí)行。
為多個操作指定回調(diào)函數(shù)如果一個回調(diào)函數(shù)需要在幾個ajax請求都成功后才能執(zhí)行該怎么辦呢?是不是不好控制呢?其實jQuery提供了這樣一個方法$.when() 它可以接收任意個deferred對象,只有所有的deferred對象都狀態(tài)都為成功時才執(zhí)行done回調(diào),否則執(zhí)行fail回調(diào)。
$.when($.ajax("test1.html"), $.ajax("test2.html")) .done(function() { // 兩個操作都請求都成功 // TODO }).fail(function() { // 任意一個失敗 // TODO });
上面代碼中,只有兩個請求都成功后才會執(zhí)行done回調(diào),只要有一個失敗就執(zhí)行fail回調(diào)。
給普通的操作指定回調(diào)函數(shù)deferred對象的最大優(yōu)點,就是它把這一套回調(diào)函數(shù)接口,從ajax操作擴展到了所有操作。也就是說,任何一個操作----不管是ajax操作還是本地操作,也不管是異步操作還是同步操作----都可以使用deferred對象的各種方法,指定回調(diào)函數(shù)。
var wait = function() { var dtd = $.Deferred(); // 新建一個deferred對象 var tasks = function() { alert("執(zhí)行完畢!"); dtd.resolve(); // 此操作完成后改變deferred對象的執(zhí)行狀態(tài) }; setTimeout(tasks, 5000); return dtd; }; // 綁定回調(diào)函數(shù) $.when(wait()) .done(function() { alert("執(zhí)行成功了!"); }) .fail(function() { alert("出錯啦!"); });
上面代碼的中的wait方法模擬了一個很耗時的操作,之后給這個操作指定了回調(diào)函數(shù)done和fail。一旦wait執(zhí)行完畢就會立即調(diào)用done這個回調(diào)函數(shù)。
Deferred對象的常用方法上面已經(jīng)對deferred對象有所了解了,下面介紹一下deferred對象的常用方法。
deferred.resolve()在jQuery的deferred對象中,規(guī)定了它有三種狀態(tài):
未完成 繼續(xù)等待
已完成 立即調(diào)用done回調(diào)
已失敗 立即調(diào)用fail回調(diào)
resolve方法的作用就是設(shè)置deferred對象狀態(tài)為已完成,deferred對象立刻調(diào)用done()方法指定的回調(diào)函數(shù)
deferred.reject()reject方法的作用是設(shè)置deferred對象狀態(tài)為已失敗,deferred對象立刻調(diào)用fail()方法指定的回調(diào)函數(shù)
deferred.done()done方法用于指定deferred對象狀態(tài)為已完成時的回調(diào)函數(shù)。
deferred.fail()done方法用于指定deferred對象狀態(tài)為已失敗時的回調(diào)函數(shù)。
deferred.then()then方法接收一到三個參數(shù),分別指定deferred對象狀態(tài)為已成功、已失敗和繼續(xù)等待的回調(diào)函數(shù)。
deferred.always()always方法用于指定deferred對象狀態(tài)為已成功或已失敗時的回調(diào)函數(shù)。
即無論這個deferred對象是成功還是失敗,只要執(zhí)行完畢都會調(diào)用此方法指定的回調(diào)。
由于此方法指定的回調(diào)函數(shù)的參數(shù)是不確定的(比如ajax請求成功和失敗返回的信息不同,成功時為返回的數(shù)據(jù),失敗則為錯誤信息),最好只使用它的行為,而不檢查其參數(shù)。如果要執(zhí)行的操作和參數(shù)有關(guān),請顯示地指定done和fail回調(diào)。如下所示:
$.ajax("test1.html") .always(function() { // 不管請求是否成功,只要請求完畢就執(zhí)行 // TODO console.log("已請求完畢,狀態(tài)未知"); }); $.ajax("test1.html") .done(function(data) { // 請求成功時執(zhí)行 // TODO console.log("請求已成功,返回數(shù)據(jù)為:"); console.log(data); }) .fail(function(error) { // 請求失敗時執(zhí)行 // TODO console.log("請求已失敗,錯誤信息:"); console.log(error.status, error.statusText); });deferred.progress()
progress方法用于指定deferred對象狀態(tài)為等待中的回調(diào)函數(shù)。但是它僅在deferred對象生成了進度通知時才會被調(diào)用。
請看下面例子:
var wait = function() { var dtd = $.Deferred(); // 新建一個deferred對象 var tasks = function() { alert("執(zhí)行完畢!"); dtd.resolve(); // 此操作完成后改變deferred對象的執(zhí)行狀態(tài) }; setTimeout(tasks, 5000); return dtd; }; // 綁定回調(diào)函數(shù) $.when(wait()) .done(function() { alert("執(zhí)行成功了!"); }) .fail(function() { alert("出錯啦!"); }) .progress(function(){ console.log("正在執(zhí)行中..."); // 此處不會有任何輸出 });
上面雖然指定了progress回調(diào),但是卻為沒有任何作用的原因是由于在deferred對象沒有生成進度通知,所以其不會被調(diào)用。
想要progress回調(diào)能執(zhí)行,需要在deferred對象上調(diào)用此回調(diào)。notify方法的作用就是根據(jù)給定的 args參數(shù) 調(diào)用Deferred對象上進行中的progress回調(diào)。
var wait = function() { var dtd = $.Deferred(); // 新建一個deferred對象 var i = 1, timer, percent; // 記錄進度 var tasks = function() { if (i == 11) { alert("執(zhí)行完畢!"); dtd.resolve(); // 此操作完成后改變deferred對象的執(zhí)行狀態(tài) } else { percent = (i * 500) / 5000 * 100 + "%"; dtd.notify(percent); // 調(diào)用progress回調(diào) i++; setTimeout(tasks, 500); } }; setTimeout(tasks, 1000); return dtd; }; // 綁定回調(diào)函數(shù) $.when(wait()) .done(function() { alert("執(zhí)行成功了!"); }) .fail(function() { alert("出錯啦!"); }) .progress(function(data) { console.log("執(zhí)行中,已完成", data); }); // 執(zhí)行中,已完成 10% // 執(zhí)行中,已完成 20% // 執(zhí)行中,已完成 30% // 執(zhí)行中,已完成 40% // 執(zhí)行中,已完成 50% // 執(zhí)行中,已完成 60% // 執(zhí)行中,已完成 70% // 執(zhí)行中,已完成 80% // 執(zhí)行中,已完成 90% // 執(zhí)行中,已完成 100% // 之后彈出 執(zhí)行完畢!和 執(zhí)行成功了!
這個方法給上傳文件或者耗時操作生成進度條提供了一種可能。
jQuery3.0以上版本對when方法做了大幅調(diào)整。向promise/A+靠齊,上面的寫法中notify是觸發(fā)不了when中的progress回調(diào)的,需要使用promise來給對象部署deferred接口或使用$.Deferred()傳入函數(shù)名。
簡而言之,3.0以以上版本中,上面代碼中progress回調(diào)是不會進去的,應使用以下寫法:
1、promise給一個對象部署Deferred接口:
var dtd = $.Deferred(); // 新建一個deferred對象 var wait = function(dtd) { var i = 1, timer, percent; // 記錄進度 var tasks = function() { if (i == 11) { alert("執(zhí)行完畢!"); dtd.resolve(); // 此操作完成后改變deferred對象的執(zhí)行狀態(tài) } else { percent = (i * 500) / 5000 * 100 + "%"; dtd.notify(percent); // 調(diào)用progress回調(diào) i++; setTimeout(tasks, 500); } }; setTimeout(tasks, 1000); }; // 在wait對象上部署Deferred接口,此后就可以直接在wait上使用deferred對象promise后的方法了 dtd.promise(wait); // 在wait對象上使用deferred對象的方法指定回調(diào)。 wait.done(function() { alert("執(zhí)行成功了!"); }) .fail(function() { alert("出錯啦!"); }) .progress(function(data) { console.log("執(zhí)行中,已完成", data); }); // 執(zhí)行 wait(dtd);
2、使用$.Deferred傳入函數(shù)名:
var wait = function(dtd) { var i = 1, timer, percent; // 記錄進度 var tasks = function() { if (i == 11) { alert("執(zhí)行完畢!"); dtd.resolve(); // 此操作完成后改變deferred對象的執(zhí)行狀態(tài) } else { percent = (i * 500) / 5000 * 100 + "%"; dtd.notify(percent); // 調(diào)用progress回調(diào) i++; setTimeout(tasks, 500); } }; setTimeout(tasks, 1000); return dtd; }; // 綁定回調(diào)函數(shù) $.Deferred(wait) .done(function() { alert("執(zhí)行成功了!"); }) .fail(function() { alert("出錯啦!"); }) .progress(function(data) { console.log("執(zhí)行中,已完成", data); });deferred.promise()
promise方法的作用是在原來的deferred對象上返回另一個deferred對象,后者只開放與改變執(zhí)行狀態(tài)無關(guān)的方法(比如done()方法和fail()方法),屏蔽與改變執(zhí)行狀態(tài)有關(guān)的方法(比如resolve()方法和reject()方法),從而使得執(zhí)行狀態(tài)不能被改變。
var dtd = $.Deferred(); // 新建一個Deferred對象 var wait = function(dtd) { var tasks = function() { alert("執(zhí)行完畢!"); dtd.resolve(); // 改變Deferred對象的執(zhí)行狀態(tài) }; setTimeout(tasks, 5000); return dtd; }; $.when(wait(dtd)) .done(function() { alert("執(zhí)行成功!"); }) .fail(function() { alert("出錯啦!"); }); dtd.reject(); // 改變狀態(tài)為失敗,將立即觸發(fā)fail 5s后完成再出發(fā)done
如果我們把deferred對象定義在了函數(shù)外部,那么我們設(shè)置deferred對象的狀態(tài)就會導致調(diào)用對應的回調(diào)。上面代碼中,最后調(diào)用reject方法,會導致立即調(diào)用了fail回調(diào),5s之后又彈出執(zhí)行完畢和執(zhí)行成功。這將會導致不必要的混亂。使用promise方法就是一種解決方案。(之前寫的將var dtd = $.Deferred()放在函數(shù)內(nèi)部,使得外部訪問不到也是一種解決方案)。
var dtd = $.Deferred(); // 新建一個Deferred對象 var wait = function(dtd) { var tasks = function() { alert("執(zhí)行完畢!"); dtd.resolve(); // 改變Deferred對象的執(zhí)行狀態(tài) }; setTimeout(tasks, 5000); return dtd.promise(); // 返回promise對象 }; var d = wait(dtd); // 新建一個d對象,改為對這個對象進行操作 $.when(d) .done(function() { alert("哈哈,成功了!"); }).fail(function() { alert("出錯啦!"); }); d.resolve(); // d.resolve is not a function 經(jīng)過promise后沒有resolve方法了
我們看一下Deferred對象和它promise之后的區(qū)別。
promise返回的對象上已經(jīng)去掉了和改變狀態(tài)有關(guān)的方法。notify和notifyWith是調(diào)用progress回調(diào),resolve和reject用于設(shè)置其狀態(tài),帶with的方法可指定上下文環(huán)境。
此方法還可以接收Object類型的參數(shù),deferred.promise()會將事件綁定到該參數(shù)上,然后返回該對象,而不是創(chuàng)建一個新的對象。 這個方法可以用于在已經(jīng)存在的對象上綁定 Promise 行為的情況。示例如下:
var dtd = $.Deferred(); // 生成Deferred對象 var wait = function(dtd) { var tasks = function() { alert("執(zhí)行完畢!"); dtd.resolve(); // 執(zhí)行完畢后改變Deferred對象的執(zhí)行狀態(tài) }; setTimeout(tasks, 5000); }; // 在wait對象上部署Deferred接口,此后就可以直接在wait上使用deferred對象promise后的方法了 dtd.promise(wait); // 在wait對象上使用deferred對象的方法指定回調(diào)。 wait.done(function() { alert("哈哈,成功了!"); }) .fail(function() { alert("出錯啦!"); }); // 執(zhí)行 wait(dtd);$.Deferred()
$.Deferred()除了創(chuàng)建一個deferred對象之外,可以接受一個函數(shù)名(注意,是函數(shù)名)作為參數(shù),$.Deferred()所生成的deferred對象將作為這個函數(shù)的默認參數(shù)。
var wait = function(dtd) { var tasks = function() { alert("執(zhí)行完畢!"); dtd.resolve(); }; setTimeout(tasks, 5000); }; $.Deferred(wait) .done(function() { alert("執(zhí)行成功!"); }) .fail(function() { alert("執(zhí)行失??!"); });$.when()
接收一個或多個deferred對象作為參數(shù),為其指定回調(diào)函數(shù)。
見為多個操作指定回調(diào)函數(shù)
deferred.then的傳遞作用之前我們介紹了,then方法接一到三個參數(shù),分別指定deferred對象狀態(tài)為已成功、已失敗和繼續(xù)等待的回調(diào)函數(shù)。
除此之外,then還可以傳遞遲延對象。我們知道,deferred可以鏈式操作的原因是其返回的仍是deferred對象。then中的回調(diào)函數(shù)如果沒有返回新的deferred對象時,將依然使用最開始的那個deferred對象,如果在其也行返回一個deferred對象時,之后的操作將被轉(zhuǎn)移到在回調(diào)函數(shù)中return出來的新的deferred對象,從而進行傳遞。
我們來看一個例子:
有5個js文件,其內(nèi)容如下:
// perosn.js var Person = function(name){ this.name= name; }; // person.prototype.js if (Person) { Person.prototype.showName = function() { return this.name; }; } else { console.error("Person is undefined!"); } // zs.js var zs = new Person("張三"); // ls.js var ls = new Person("李四"); // introduce.js var introduceEachOther = function() { console.log("張三:你好,我叫" + zs.showName()); console.log("李四:你好,我叫" + ls.showName()); };
文件內(nèi)容都非常簡單,僅做演示使用,但是其中的依賴關(guān)系非常明顯,person.prototype.js依賴于perosn.js,zs.js和ls.js依賴于person.js,introduce.js依賴于其他所有的js。
要分步加載,并保證可用,就必須保證在加載當前js時,其依賴的js已經(jīng)加載完畢。
使用傳統(tǒng)的寫法,勢必會嵌套多層,不僅邏輯不好處理,而且可讀性很差。
我們用then演示一下如何來傳遞遞延對象來完成這個操作。
$.ajax({ url: "person.js", dataType: "script" }).then(function() { console.log(Person); // person.js已經(jīng)加載成功 console.log("person.js已經(jīng)加載成功"); return $.ajax({ url: "person.prototype.js", dataType: "script" }); }).then(function(data) { // 這里的data 上一步請求文件的內(nèi)容 是person.prototype.js而非person.js console.log(data); console.log(Person.prototype.showName); console.log("person.prototype.js已經(jīng)加載成功"); // person.prototype.js 已經(jīng)加載 return $.when($.ajax({ url: "zs.js", dataType: "script" }), $.ajax({ url: "ls.js", dataType: "script" })); }).then(function(){ // zs.js 和 ls.js 都加載完成 console.log(zs,ls); console.log("zs.js和zs.js已經(jīng)加載成功"); return $.ajax({ url: "introduce.js", dataType: "script" }); },function(){ console.log("zs.js or ls.js failed"); }).then(function(){ // 到此前面的所有資源都是加載成功的 console.log("introduce.js已經(jīng)加載成功,且其依賴資源都加載成功了,可以使用了"); introduceEachOther(); });
在線地址
以上處理順序只是舉例演示,只要保證person.js最先加載,introduce.js最后加載,person.prototype.js在person.js之后即可。ls.js、zs.js與person.prototype.js的順序可以調(diào)整。
我們每次在then的第一個回調(diào)中返回了而一個新的遞延對象,之后的操作就是基于你返回的那個遞延對象了,第二個then回調(diào)中輸出的data證明了這一點。(如果沒有return出一個新的遞延對象的話,依然使用之前的那個。)
參考鏈接:本文是照著下面的文章加上自己的學習體會書寫的。
阮一峰:jQuery的deferred對象詳解
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/82273.html
摘要:中文文檔簡單說,對象就是的回調(diào)函數(shù)解決方案。為了讓回調(diào)函數(shù)的名字統(tǒng)一,便于在中使用。普通操作的回調(diào)函數(shù)接口對象的最大優(yōu)點,就是它把這一套回調(diào)函數(shù)接口,從操作擴展到了所有操作。指定操作成功時的回調(diào)函數(shù)。 參考鏈接 jQuery API中文文檔 jQuery.Deferred jQuery.when jQuery的deferred對象詳解 jQuery deferred 對象的 prom...
摘要:通常的做法是,為它們指定回調(diào)函數(shù)。指定操作成功時的回調(diào)函數(shù)指定操作失敗時的回調(diào)函數(shù)沒有參數(shù)時,返回一個新的對象,該對象的運行狀態(tài)無法被改變接受參數(shù)時,作用為在參數(shù)對象上部署接口。 jQuery的開發(fā)速度很快,幾乎每半年一個大版本,每兩個月一個小版本。 每個版本都會引入一些新功能。今天我想介紹的,就是從jQuery 1.5.0版本開始引入的一個新功能----deferred對象。 這個功...
摘要:通常的做法是,為它們指定回調(diào)函數(shù)。簡單說,對象就是的回調(diào)函數(shù)解決方案。指定操作成功時的回調(diào)函數(shù)指定操作失敗時的回調(diào)函數(shù)沒有參數(shù)時,返回一個新的對象,該對象的運行狀態(tài)無法被改變接受參數(shù)時,作用為在參數(shù)對象上部署接口。 轉(zhuǎn)自:阮一峰:http://www.ruanyifeng.com/blo... 一、什么是deferred對象?開發(fā)網(wǎng)站的過程中,我們經(jīng)常遇到某些耗時很長的javascri...
摘要:和和都有和,但是略有不同。實際上返回的是一個對象。和添加的回調(diào),添加的回調(diào)。所以在調(diào)用成功的情況下執(zhí)行添加的回調(diào),調(diào)用失敗時執(zhí)行添加的回調(diào)。,產(chǎn)生對象并,產(chǎn)生對象并,然后繼續(xù)處理,的語法糖,和的差不多但不同。 Deferred 和 Promise ES6 和 jQuery 都有 Deffered 和 Promise,但是略有不同。不過它們的作用可以簡單的用兩句話來描述 Deffere...
摘要:回調(diào)隊列對象,用于構(gòu)建易于操作的回調(diào)函數(shù)集合,在操作完成后進行執(zhí)行。對象對象,用于管理回調(diào)函數(shù)的多用途列表。如果傳入一個延遲對象,則返回該對象的對象,可以繼續(xù)綁定其余回調(diào),在執(zhí)行結(jié)束狀態(tài)之后也同時調(diào)用其回調(diào)函數(shù)。 在工作中我們可能會把jQuery選擇做自己項目的基礎(chǔ)庫,因為其提供了簡便的DOM選擇器以及封裝了很多實用的方法,比如$.ajax(),它使得我們不用操作xhr和xdr對象,直...
摘要:我們稱為回調(diào)對象,它內(nèi)部會維護一個數(shù)組,我們可以向其中添加若干個回調(diào)函數(shù),然后在某一條件下觸發(fā)執(zhí)行。第一次之后,再次新的回調(diào)函數(shù)時,自動執(zhí)行回調(diào)。當前面的回調(diào)函數(shù)返回時,終止后面的回調(diào)繼續(xù)執(zhí)行。 最近懶癌發(fā)作,說好的系列文章,寫了一半,一直懶得寫,今天補上一篇。 Deferred 我們在使用promise對象時,總會提到一個與它關(guān)系密切的對象——Deferred。其實Deferred沒...
閱讀 2978·2021-10-20 13:46
閱讀 2524·2021-08-12 13:22
閱讀 2710·2019-08-30 15:54
閱讀 2348·2019-08-30 15:53
閱讀 553·2019-08-30 13:47
閱讀 3587·2019-08-23 16:56
閱讀 1733·2019-08-23 13:02
閱讀 1802·2019-08-23 12:25