JQuery中的Deferred-详解和使用
?
首先,為什么要使用Deferred?
先來(lái)看一段AJAX的代碼:
1 var data; 2 $.get('api/data', function(resp) { 3 data = resp.data; 4 }); 5 doSomethingFancyWithData(data); View Code這段代碼極容易出問(wèn)題,請(qǐng)求時(shí)間多長(zhǎng)或者超時(shí),將會(huì)導(dǎo)致我們獲取不到data。只有把請(qǐng)求設(shè)置為同步我們才能夠等待獲取到data,才執(zhí)行我們的函數(shù)。但是這會(huì)帶來(lái)阻塞,導(dǎo)致用戶界面一直被凍結(jié),對(duì)用戶體驗(yàn)有很?chē)?yán)重的影響。所以我們需要使用異步編程,
JS的異步編程有兩種方式基于事件和基于回調(diào),
傳統(tǒng)的異步編程會(huì)帶來(lái)的一些問(wèn)題,
1.序列化異步操作導(dǎo)致的問(wèn)題:
1),延續(xù)傳遞風(fēng)格Continuation Passing Style (CPS)
? ? ? 2),深度嵌套
? ? ?3),回調(diào)地獄
2.并行異步操作的困難
?
下面是一段序列化異步操作的代碼:
1 // Demonstrates nesting, CPS, 'callback hell' 2 $.get('api1/data', function(resp1) { 3 // Next that depended on the first response. 4 $.get('api2/data', function(resp2) { 5 // Next request that depended on the second response. 6 $.get('api3/data', function(resp3) { 7 // Next request that depended on the third response. 8 $.get(); // ... you get the idea. 9 }); 10 }); 11 }); View Code當(dāng)回調(diào)越來(lái)越多,嵌套越深,代碼可讀性就會(huì)越來(lái)越差。如果注冊(cè)了多個(gè)回調(diào),那更是一場(chǎng)噩夢(mèng)!
再看另一段有關(guān)并行化異步操作的代碼:
$.get('api1/data', function(resp1) { trackMe(); }); $.get('api2/data', function(resp2) { trackMe(); }); $.get('api3/data', function(resp3) { trackMe(); }); var trackedCount = 0; function trackMe() {++trackedCount;if (trackedCount === 3) {doSomethingThatNeededAllThree();} } View Code上面的代碼意思是當(dāng)三個(gè)請(qǐng)求都成功就執(zhí)行我們的函數(shù)(只執(zhí)行一次),毫無(wú)疑問(wèn),這段代碼有點(diǎn)繁瑣,而且如果我們要添加失敗回調(diào)將會(huì)是一件很麻煩的事情。
?
我們需要一個(gè)更好的規(guī)范,那就是Promise規(guī)范,這里引用Aaron的一篇文章中的一段,http://www.cnblogs.com/aaronjs/p/3163786.html:
- 在我開(kāi)始promise的“重點(diǎn)”之前,我想我應(yīng)該給你一點(diǎn)它們?nèi)绾喂ぷ鞯膬?nèi)貌。一個(gè)promise是一個(gè)對(duì)象——根據(jù)Promise/A規(guī)范——只需要一個(gè)方法:then。then方法帶有三個(gè)參數(shù):一個(gè)成功回調(diào),一個(gè)失敗回調(diào),和一個(gè)前進(jìn)回調(diào)(規(guī)范沒(méi)有要求包括前進(jìn)回調(diào)的實(shí)現(xiàn),但是很多都實(shí)現(xiàn)了)。一個(gè)全新的promise對(duì)象從每個(gè)then的調(diào)用中返回。
- 一個(gè)promise可以是三種狀態(tài)之一:未完成的,完成的,或者失敗的。promise以未完成的狀態(tài)開(kāi)始,如果成功它將會(huì)是完成態(tài),如果失敗將會(huì)是失敗態(tài)。當(dāng)一個(gè)promise移動(dòng)到完成態(tài),所有注冊(cè)到它的成功回調(diào)將被調(diào)用,而且會(huì)將成功的結(jié)果值傳給它。另外,任何注冊(cè)到promise的成功回調(diào),將會(huì)在它已經(jīng)完成以后立即被調(diào)用。
- 同樣的事情發(fā)生在promise移動(dòng)到失敗態(tài)的時(shí)候,除了它調(diào)用的是失敗回調(diào)而不是成功回調(diào)。對(duì)包含前進(jìn)特性的實(shí)現(xiàn)來(lái)說(shuō),promise在它離開(kāi)未完成狀態(tài)以前的任何時(shí)刻,都可以更新它的progress。當(dāng)progress被更新,所有的前進(jìn)回調(diào)(progress callbacks)會(huì)被傳遞以progress的值,并被立即調(diào)用。前進(jìn)回調(diào)被以不同于成功和失敗回調(diào)的方式處理;如果你在一個(gè)progress更新已經(jīng)發(fā)生以后注冊(cè)了一個(gè)前進(jìn)回調(diào),新的前進(jìn)回調(diào)只會(huì)在它被注冊(cè)以后被已更新的progress調(diào)用。
- 我們不會(huì)進(jìn)一步深入promise狀態(tài)是如何管理的,因?yàn)槟遣辉谝?guī)范之內(nèi),而且每個(gè)實(shí)現(xiàn)都有差別。在后面的例子中,你將會(huì)看到它是如何完成的,但目前這就是所有你需要知道的。
?
現(xiàn)在有不少庫(kù)已經(jīng)實(shí)現(xiàn)了Deferred的操作,其中jQuery的Deferred就非常熱門(mén):
先過(guò)目一下Deferred的API:
?
jQuery的有關(guān)Deferred的API簡(jiǎn)介:
1 $.ajax('data/url') 2 .done(function(response, statusText, jqXHR){ 3 console.log(statusText); 4 }) 5 .fail(function(jqXHR, statusText, error){ 6 console.log(statusText); 7 }) 8 ,always(function(){ 9 console.log('I will always done.'); 10 }); View Code1.done,fail,progress都是給回調(diào)列表添加回調(diào),因?yàn)閖Query的Deferred內(nèi)部使用了其$.Callbacks對(duì)象,并且增加了memory的標(biāo)記(詳情請(qǐng)查看我的這篇文章jQuery1.9.1源碼分析--Callbacks對(duì)象),
所以如果我們第一次觸發(fā)了相應(yīng)的回調(diào)列表的回調(diào)即調(diào)用了resolve,resolveWith,reject,rejectWith或者notify,notifyWith這些相應(yīng)的方法,當(dāng)我們?cè)俅谓o該回調(diào)列表添加回調(diào)時(shí),就會(huì)立刻觸發(fā)該回調(diào)了,
即使用了done,fail,progress這些方法,而不需要我們手動(dòng)觸發(fā)。jQuery的ajax會(huì)在請(qǐng)求完成后就會(huì)觸發(fā)相應(yīng)的回調(diào)列表。所以我們后面的鏈?zhǔn)讲僮鞯淖?cè)回調(diào)有可能是已經(jīng)觸發(fā)了回調(diào)列表才添加的,所以它們就會(huì)立刻被執(zhí)行。
2.always方法則是不管成功還是失敗都會(huì)執(zhí)行該回調(diào)。
接下來(lái)要介紹重量級(jí)的then方法(也是pipe方法):
3.then方法會(huì)返回一個(gè)新的Deferred對(duì)象
* 如果then方法的參數(shù)是deferred對(duì)象,
* 上一鏈的舊deferred會(huì)調(diào)用[ done | fail | progress ]方法注冊(cè)回調(diào),該回調(diào)內(nèi)容是:執(zhí)行then方法對(duì)應(yīng)的參數(shù)回調(diào)(fnDone, fnFail, fnProgress)。
* 1)如果參數(shù)回調(diào)執(zhí)行后返回的結(jié)果是一個(gè)promise對(duì)象,我們就給該promise對(duì)象相應(yīng)的回調(diào)列表添加回調(diào),該回調(diào)是觸發(fā)then方法返回的新promise對(duì)象的成功,失敗,處理中(done,fail,progress)的回調(diào)列表中的所有回調(diào)。
* 當(dāng)我們?cè)俳othen方法進(jìn)行鏈?zhǔn)降靥砑踊卣{(diào)操作(done,fail,progress,always,then)時(shí),就是給新deferred對(duì)象注冊(cè)回調(diào)到相應(yīng)的回調(diào)列表。
* 如果我們then參數(shù)fnDoneDefer, fnFailDefer, fnProgressDefer得到了解決,就會(huì)執(zhí)行后面鏈?zhǔn)教砑踊卣{(diào)操作中的參數(shù)函數(shù)。
*
* 2)如果參數(shù)回調(diào)執(zhí)行后返回的結(jié)果returned不是promise對(duì)象,就立刻觸發(fā)新deferred對(duì)象相應(yīng)回調(diào)列表的所有回調(diào),且回調(diào)函數(shù)的參數(shù)是先前的執(zhí)行返回結(jié)果returned。
* 當(dāng)我們?cè)俳othen方法進(jìn)行鏈?zhǔn)降靥砑踊卣{(diào)操作(done,fail,progress,always,then)時(shí),就會(huì)立刻觸發(fā)我們添加的相應(yīng)的回調(diào)。
*
* 可以多個(gè)then連續(xù)使用,此功能相當(dāng)于順序調(diào)用異步回調(diào)。
?
1 $.ajax({ 2 url: 't2.html', 3 dataType: 'html', 4 data: { 5 d: 4 6 } 7 }).then(function(){ 8 console.log('success'); 9 },function(){ 10 console.log('failed'); 11 }).then(function(){ 12 console.log('second'); 13 return $.ajax({ 14 url: 'jquery-1.9.1.js', 15 dataType: 'script' 16 }); 17 }, function(){ 18 console.log('second f'); 19 return $.ajax({ 20 url: 'jquery-1.9.1.js', 21 dataType: 'script' 22 }); 23 }).then(function(){ 24 console.log('success2'); 25 },function(){ 26 console.log('failed2'); 27 }); View Code上面的代碼,如果第一個(gè)對(duì)t2.html的請(qǐng)求成功輸出success,就會(huì)執(zhí)行second的ajax請(qǐng)求,接著針對(duì)該請(qǐng)求是成功還是失敗,執(zhí)行success2或者failed2。
如果第一個(gè)失敗輸出failed,然后執(zhí)行second f的ajax請(qǐng)求(注意和上面的不一樣),接著針對(duì)該請(qǐng)求是成功還是失敗,執(zhí)行success2或者failed2。
理解這些對(duì)失敗處理很重要。
?
將我們上面序列化異步操作的代碼使用then方法改造后,代碼立馬變得扁平化了,可讀性也增強(qiáng)了:
1 var req1 = $.get('api1/data'); 2 var req2 = $.get('api2/data'); 3 var req3 = $.get('api3/data'); 4 5 req1.then(function(req1Data){ 6 return req2.done(otherFunc); 7 }).then(function(req2Data){ 8 return req3.done(otherFunc2); 9 }).then(function(req3Data){ 10 doneSomethingWithReq3(); 11 }); View Code?
4.接著介紹$.when的方法使用,主要是對(duì)多個(gè)deferred對(duì)象進(jìn)行并行化操作,當(dāng)所有deferred對(duì)象都得到解決就執(zhí)行后面添加的相應(yīng)回調(diào)。
1 $.when( 2 $.ajax({ 3 4 url: 't2.html' 5 6 }), 7 $.ajax({ 8 url: 'jquery-1.9.1-study.js' 9 }) 10 ).then(function(FirstAjaxSuccessCallbackArgs, SecondAjaxSuccessCallbackArgs){ 11 console.log('success'); 12 }, function(){ 13 console.log('failed'); 14 }); View Code如果有一個(gè)失敗了都會(huì)執(zhí)行失敗的回調(diào)。
將我們上面并行化操作的代碼改良后:
1 $.when( 2 $.get('api1/data'), 3 $.get('api2/data'), 4 $.get('api3/data'), 5 { key: 'value' } 6 ).done(); View Code?
5.promse方法是返回的一個(gè)promise對(duì)象,該對(duì)象只能添加回調(diào)或者查看狀態(tài),但不能觸發(fā)。我們通常將該方法暴露給外層使用,而內(nèi)部應(yīng)該使用deferred來(lái)觸發(fā)回調(diào)。
?
如何使用deferred封裝異步函數(shù)
?第一種:
1 function getData(){ 2 // 1) create the jQuery Deferred object that will be used 3 var deferred = $.Deferred(); 4 // ---- AJAX Call ---- // 5 var xhr = new XMLHttpRequest(); 6 xhr.open("GET","data",true); 7 8 // register the event handler 9 xhr.addEventListener('load',function(){ 10 if(xhr.status === 200){ 11 // 3.1) RESOLVE the DEFERRED (this will trigger all the done()...) 12 deferred.resolve(xhr.response); 13 }else{ 14 // 3.2) REJECT the DEFERRED (this will trigger all the fail()...) 15 deferred.reject("HTTP error: " + xhr.status); 16 } 17 },false) 18 19 // perform the work 20 xhr.send(); 21 // Note: could and should have used jQuery.ajax. 22 // Note: jQuery.ajax return Promise, but it is always a good idea to wrap it 23 // with application semantic in another Deferred/Promise 24 // ---- /AJAX Call ---- // 25 26 // 2) return the promise of this deferred 27 return deferred.promise(); 28 } View Code第二種方法:
1 function prepareInterface() { 2 return $.Deferred(function( dfd ) { 3 var latest = $( “.news, .reactions” ); 4 latest.slideDown( 500, dfd.resolve ); 5 latest.addClass( “active” ); 6 }).promise(); 7 } View Code?
?
Deferred的一些使用技巧:
?1.異步緩存
以ajax請(qǐng)求為例,緩存機(jī)制需要確保我們的請(qǐng)求不管是否已經(jīng)存在于緩存,只能被請(qǐng)求一次。?因此,為了緩存系統(tǒng)可以正確地處理請(qǐng)求,我們最終需要寫(xiě)出一些邏輯來(lái)跟蹤綁定到給定url上的回調(diào)。
?
1 var cachedScriptPromises = {}; 2 3 $.cachedGetScript = function(url, callback){ 4 if(!cachedScriptPromises[url]) { 5 cachedScriptPromises[url] = $.Deferred(function(defer){ 6 $.getScript(url).then(defer.resolve, defer.reject); 7 }).promise(); 8 } 9 10 return cachedScriptPromises[url].done(callback); 11 }; View Code?
我們?yōu)槊恳粋€(gè)url緩存一個(gè)promise對(duì)象。?如果給定的url沒(méi)有promise,我們創(chuàng)建一個(gè)deferred,并發(fā)出請(qǐng)求。?如果它已經(jīng)存在我們只需要為它綁定回調(diào)。?該解決方案的一大優(yōu)勢(shì)是,它會(huì)透明地處理新的和緩存過(guò)的請(qǐng)求。?另一個(gè)優(yōu)點(diǎn)是一個(gè)基于deferred的緩存?會(huì)優(yōu)雅地處理失敗情況。?當(dāng)promise以‘rejected’狀態(tài)結(jié)束的話,我們可以提供一個(gè)錯(cuò)誤回調(diào)來(lái)測(cè)試:
$.cachedGetScript(?url?).then(?successCallback,?errorCallback?);
請(qǐng)記住:無(wú)論請(qǐng)求是否緩存過(guò),上面的代碼段都會(huì)正常運(yùn)作!
?
通用異步緩存
為了使代碼盡可能的通用,我們建立一個(gè)緩存工廠并抽象出實(shí)際需要執(zhí)行的任務(wù)
1 $.createCache = function(requestFunc){ 2 var cache = {}; 3 4 return function(key, callback){ 5 if(!cache[key]) { 6 cache[key] = $.Deferred(function(defer){ 7 requestFunc(defer, key); 8 }).promise(); 9 } 10 11 return cache[key].done(callback); 12 }; 13 }; 14 15 16 // 現(xiàn)在具體的請(qǐng)求邏輯已經(jīng)抽象出來(lái),我們可以重新寫(xiě)cachedGetScript: 17 $.cachedGetScript = $.createCache(function(defer, url){ 18 $.getScript(url).then(defer.resolve, defer.reject); 19 }); View Code我們可以使用這個(gè)通用的異步緩存很輕易的實(shí)現(xiàn)一些場(chǎng)景:
圖片加載
1 // 確保我們不加載同一個(gè)圖像兩次 2 $.loadImage = $.createCache(function(defer, url){ 3 var image = new Image(); 4 function clearUp(){ 5 image.onload = image.onerror = null; 6 } 7 defer.then(clearUp, clearUp); 8 image.onload = function(){ 9 defer.resolve(url); 10 }; 11 image.onerror = defer.reject; 12 image.src = url; 13 }); 14 15 // 無(wú)論image.png是否已經(jīng)被加載,或者正在加載過(guò)程中,緩存都會(huì)正常工作。 16 $.loadImage( "my-image.png" ).done( callback1 ); 17 $.loadImage( "my-image.png" ).done( callback1 ); View Code緩存響應(yīng)數(shù)據(jù)
1 $.searchTwitter = $.createCache(function(defer, query){ 2 $.ajax({ 3 url: 'http://search.twitter.com/search.json', 4 data: {q: query}, 5 dataType: 'jsonp' 6 }).then(defer.resolve, defer.reject); 7 }); 8 9 // 在Twitter上進(jìn)行搜索,同時(shí)緩存它們 10 $.searchTwitter( "jQuery Deferred", callback1 ); View Code定時(shí),?
基于deferred的緩存并不限定于網(wǎng)絡(luò)請(qǐng)求;它也可以被用于定時(shí)目的。
?
1 // 新的afterDOMReady輔助方法用最少的計(jì)數(shù)器提供了domReady后的適當(dāng)時(shí)機(jī)。 如果延遲已經(jīng)過(guò)期,回調(diào)會(huì)被馬上執(zhí)行。 2 $.afterDOMReady = (function(){ 3 var readyTime; 4 5 $(function(){ 6 readyTime = (new Date()).getTime(); 7 }); 8 9 return $.createCache(function(defer, delay){ 10 delay = delay || 0; 11 12 $(function(){ 13 var delta = (new Date()).getTime() - readyTime; 14 15 if(delta >= delay) { 16 defer.resolve(); 17 } else { 18 setTimeout(defer.resolve, delay - delta); 19 } 20 }); 21 }); 22 })(); View Code?
?
2.同步多個(gè)動(dòng)畫(huà)
1 var fadeLi1Out = $('ul > li').eq(0).animate({ 2 opacity: 0 3 }, 1000); 4 var fadeLi2In = $('ul > li').eq(1).animate({ 5 opacity: 1 6 }, 2000); 7 8 // 使用$.when()同步化不同的動(dòng)畫(huà) 9 $.when(fadeLi1Out, fadeLi2In).done(function(){ 10 alert('done'); 11 }); View Code雖然jQuery1.6以上的版本已經(jīng)把deferred包裝到動(dòng)畫(huà)里了,但如果我們想要手動(dòng)實(shí)現(xiàn),也是一件很輕松的事:
1 $.fn.animatePromise = function( prop, speed, easing, callback ) { 2 var elements = this; 3 4 return $.Deferred(function( defer ) { 5 elements.animate( prop, speed, easing, function() { 6 defer.resolve(); 7 if ( callback ) { 8 callback.apply( this, arguments ); 9 } 10 }); 11 }).promise(); 12 }; 13 14 // 我們也可以使用同樣的技巧,建立了一些輔助方法: 15 $.each([ "slideDown", "slideUp", "slideToggle", "fadeIn", "fadeOut", "fadeToggle" ], 16 function( _, name ) { 17 $.fn[ name + "Promise" ] = function( speed, easing, callback ) { 18 var elements = this; 19 return $.Deferred(function( defer ) { 20 elements[ name ]( speed, easing, function() { 21 defer.resolve(); 22 if ( callback ) { 23 callback.apply( this, arguments ); 24 } 25 }); 26 }).promise(); 27 }; 28 }); View Code?
?
3.一次性事件
例如,您可能希望有一個(gè)按鈕,當(dāng)它第一次被點(diǎn)擊時(shí)打開(kāi)一個(gè)面板,面板打開(kāi)之后,執(zhí)行特定的初始化邏輯。?在處理這種情況時(shí),通常會(huì)這樣寫(xiě)代碼:
1 var buttonClicked = false; 2 $( "#myButton" ).click(function() { 3 if ( !buttonClicked ) { 4 buttonClicked = true; 5 initializeData(); 6 showPanel(); 7 } 8 }); View Code這是一個(gè)非常耦合的解決辦法。?如果你想添加一些其他的操作,你必須編輯綁定代碼或拷貝一份。?如果你不這樣做,你唯一的選擇是測(cè)試buttonClicked。由于buttonClicked可能是false,新的代碼可能永遠(yuǎn)不會(huì)被執(zhí)行,因此你?可能會(huì)失去這個(gè)新的動(dòng)作。
使用deferreds我們可以做的更好?(為簡(jiǎn)化起見(jiàn),下面的代碼將只適用于一個(gè)單一的元素和一個(gè)單一的事件類(lèi)型,但它可以很容易地?cái)U(kuò)展為多個(gè)事件類(lèi)型的集合):
?
1 $.fn.bindOnce = function(event, callback){ 2 var element = this; 3 defer = element.data('bind_once_defer_' + event); 4 5 if(!defer) { 6 defer = $.Deferred(); 7 8 function deferCallback(){ 9 element.off(event, deferCallback); 10 defer.resolveWith(this, arguments); 11 } 12 13 element.on(event, deferCallback); 14 element.data('bind_once_defer_' + event, defer); 15 } 16 17 return defer.done(callback).promise(); 18 }; 19 20 $.fn.firstClick = function( callback ) { 21 return this.bindOnce( "click", callback ); 22 }; 23 24 var openPanel = $( "#myButton" ).firstClick(); 25 openPanel.done( initializeData ); 26 openPanel.done( showPanel ); View Code?
該代碼的工作原理如下:
·?檢查該元素是否已經(jīng)綁定了一個(gè)給定事件的deferred對(duì)象
·?如果沒(méi)有,創(chuàng)建它,使它在觸發(fā)該事件的第一時(shí)間解決
·?然后在deferred上綁定給定的回調(diào)并返回promise
4.多個(gè)組合使用
單獨(dú)看以上每個(gè)例子,deferred的作用是有限的?。?然而,deferred真正的力量是把它們混合在一起。
*在第一次點(diǎn)擊時(shí)加載面板內(nèi)容并打開(kāi)面板
?
假如,我們有一個(gè)按鈕,可以打開(kāi)一個(gè)面板,請(qǐng)求其內(nèi)容然后淡入內(nèi)容。使用我們前面定義的方法,我們可以這樣做:
1 var panel = $('#myPanel'); 2 panel.firstClick(function(){ 3 $.when( 4 $.get('panel.html'), 5 panel.slideDown() 6 ).done(function(ajaxArgs){ 7 panel.html(ajaxArgs[0]).fadeIn(); 8 }); 9 }); View Code*在第一次點(diǎn)擊時(shí)載入圖像并打開(kāi)面板
假如,我們已經(jīng)的面板有內(nèi)容,但我們只希望當(dāng)?shù)谝淮螁螕舭粹o時(shí)加載圖像并且當(dāng)所有圖像加載成功后淡入圖像。HTML代碼如下:
1 <div id="myPanel"> 2 <img data-src="image1.png" /> 3 <img data-src="image2.png" /> 4 <img data-src="image3.png" /> 5 <img data-src="image4.png" /> 6 </div> 7 8 /* 9 我們使用data-src屬性描述圖片的真實(shí)路徑。 那么使用deferred來(lái)解決該用例的代碼如下: 10 */ 11 $('#myBtn').firstClick(function(){ 12 var panel = $('#myPanel'); 13 var promises = []; 14 15 $('img', panel).each(function(){ 16 var image = $(this); 17 var src = element.data('src'); 18 19 if(src) { 20 promises.push( 21 $.loadImage(src).then(function(){ 22 image.attr('src', src); 23 }, function(){ 24 image.attr('src', 'error.png'); 25 }) 26 ); 27 } 28 }); 29 30 promises.push(panel.slideDown); 31 32 $.when.apply(null, promises).done(function(){ 33 panel.fadeIn(); 34 }); 35 }); View Code*在特定延時(shí)后加載頁(yè)面上的圖像
假如,我們要在整個(gè)頁(yè)面實(shí)現(xiàn)延遲圖像顯示。?要做到這一點(diǎn),我們需要的HTML的格式如下:
?
1 <img data-src="image1.png" data-after="1000" src="placeholder.png" /> 2 <img data-src="image2.png" data-after="1000" src="placeholder.png" /> 3 <img data-src="image1.png" src="placeholder.png" /> 4 <img data-src="image2.png" data-after="2000" src="placeholder.png" /> 5 6 /* 7 意思非常簡(jiǎn)單: 8 image1.png,第三個(gè)圖像立即顯示,一秒后第一個(gè)圖像顯示 9 image2.png 一秒鐘后顯示第二個(gè)圖像,兩秒鐘后顯示第四個(gè)圖像 10 */ 11 12 $( "img" ).each(function() { 13 var element = $( this ), 14 src = element.data( "src" ), 15 after = element.data( "after" ); 16 if ( src ) { 17 $.when( 18 $.loadImage( src ), 19 $.afterDOMReady( after ) 20 ).then(function() { 21 element.attr( "src", src ); 22 }, function() { 23 element.attr( "src", "error.png" ); 24 } ).done(function() { 25 element.fadeIn(); 26 }); 27 } 28 }); 29 30 // 如果我們想延遲加載的圖像本身,代碼會(huì)有所不同: 31 $( "img" ).each(function() { 32 var element = $( this ), 33 src = element.data( "data-src" ), 34 after = element.data( "data-after" ); 35 if ( src ) { 36 $.afterDOMReady( after, function() { 37 $.loadImage( src ).then(function() { 38 element.attr( "src", src ); 39 }, function() { 40 element.attr( "src", "error.png" ); 41 } ).done(function() { 42 element.fadeIn(); 43 }); 44 } ); 45 } 46 }); View Code?
這里,我們首先在嘗試加載圖片之前等待延遲條件滿足。當(dāng)你想在頁(yè)面加載時(shí)限制網(wǎng)絡(luò)請(qǐng)求的數(shù)量會(huì)非常有意義。
?
?
Deferred的使用場(chǎng)所:
- Ajax(XMLHttpRequest)
- Image Tag,Script Tag,iframe(原理類(lèi)似)
- setTimeout/setInterval
- CSS3 Transition/Animation
- HTML5 Web Database
- postMessage
- Web Workers
- Web Sockets
- and more…
?
轉(zhuǎn)載于:https://www.cnblogs.com/dylanblog/p/7169069.html
總結(jié)
以上是生活随笔為你收集整理的JQuery中的Deferred-详解和使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 圆环同心度测量halcon
- 下一篇: halcon圆环完整度检测