Jquery ajax 同步阻塞引起的UI線程阻塞問(wèn)題
最近做一個(gè)項(xiàng)目,遇到了一個(gè)問(wèn)題同步ajax引起的ui線程阻塞問(wèn)題,下面把我的問(wèn)題解決過(guò)程分享給大家。
事情起因是這樣的,因?yàn)轫?yè)面上有多個(gè)相似的異步請(qǐng)求動(dòng)作,本著提高代碼可重用性的原則,我封裝了一個(gè)名為getData的函數(shù),它接收不同參數(shù),只負(fù)責(zé)獲取數(shù)據(jù),然后把數(shù)據(jù)return?;镜倪壿媱冸x出來(lái)是這樣的:
function getData1(){
var result;
$.ajax({
url : "p.php",
async : false,
success: function(data){
result = data;
}
});
return result;
}
這里的ajax不能用異步的,否則函數(shù)返回時(shí),result還未賦值,會(huì)出錯(cuò)。所以我加了async:false??雌饋?lái)好像沒(méi)什么問(wèn)題。我調(diào)用這個(gè)函數(shù)可以正常的得到數(shù)據(jù)。
$(".btn1").click(function(){
var data = getData1();
alert(data);
});
接下來(lái),要加另外一個(gè)功能,由于ajax請(qǐng)求有一定的耗時(shí),所以我需要在發(fā)出請(qǐng)求前頁(yè)面有個(gè)loading效果,即顯示一張“正在加載”的gif圖片,想必大家也都見(jiàn)過(guò)。所以我的處理函數(shù)就變成了這樣:
$(".btn1").click(function(){
$(".loadingicon").show();
var data = getData1();
$(".loadingicon").hide();
alert(data);
});
請(qǐng)求之前顯示loading圖片,請(qǐng)求完成后把它隱藏。看起來(lái)也沒(méi)什么問(wèn)題。為了看清效果,我的p.php代碼sleep了3秒,如下:
<?php
sleep(3);
echo ("aaaaaa");
?>
但是我運(yùn)行的時(shí)候問(wèn)題出現(xiàn)了,我點(diǎn)擊按鈕并未像預(yù)想的那樣出現(xiàn)這個(gè)loading圖片,頁(yè)面什么反應(yīng)也沒(méi)有。排除良久找到了原因,就在async:false這里。
瀏覽器的渲染(UI)線程和js線程是互斥的,在執(zhí)行js耗時(shí)操作時(shí),頁(yè)面渲染會(huì)被阻塞掉。當(dāng)我們執(zhí)行異步ajax的時(shí)候沒(méi)有問(wèn)題,但當(dāng)設(shè)置為同步請(qǐng)求時(shí),其他的動(dòng)作(ajax函數(shù)后面的代碼,還有渲染線程)都會(huì)停止下來(lái)。即使我的DOM操作語(yǔ)句是在發(fā)起請(qǐng)求的前一句,這個(gè)同步請(qǐng)求也會(huì)“迅速”將UI線程阻塞,不給它執(zhí)行的時(shí)間。這就是代碼失效的原因。
setTimeout解決阻塞問(wèn)題
既然明白了問(wèn)題在哪里,我們就來(lái)針對(duì)性想辦法。為了不讓同步ajax請(qǐng)求阻塞線程,我想到了setTimeout,把請(qǐng)求的代碼放到sestTimeout中,讓瀏覽器重啟一個(gè)線程來(lái)操作,不就解決問(wèn)題了嗎?于是乎,我的代碼就變成了這樣:
$(".btn2").click(function(){
$(".loadingicon").show();
setTimeout(function(){
$.ajax({
url : "p.php",
async : false,
success: function(data){
$(".loadingicon").hide();
alert(data);
}
});
}, 0);
});
setTimeout的第二個(gè)參數(shù)設(shè)為0,瀏覽器會(huì)在一個(gè)已設(shè)的最小時(shí)間后執(zhí)行。不管三七二十一先運(yùn)行起來(lái)看看。
結(jié)果loading圖片顯示出來(lái)了,但是?。?!圖片怎么不動(dòng)呢,我明明是一張動(dòng)態(tài)gif圖。這個(gè)時(shí)候我很快就想到了,雖然同步請(qǐng)求延遲執(zhí)行了,但是它執(zhí)行期間還是會(huì)把UI線程給阻塞。這個(gè)阻塞相當(dāng)牛逼,連gif圖片都不動(dòng)了,看起來(lái)像一張靜態(tài)圖片一樣。
結(jié)論很明顯,setTimeout治標(biāo)不治本,相當(dāng)于把同步請(qǐng)求“稍稍”異步了一下,接下來(lái)還是會(huì)進(jìn)入同步的噩夢(mèng),阻塞線程。方案失敗。
是時(shí)候用Deferred了
jQuery在1.5版本之后,引入了Deferred對(duì)象,提供的很方便的廣義異步機(jī)制。詳情可參看阮一峰老師的這篇文章http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html。
于是我用Deferred對(duì)象改寫(xiě)了代碼,如下:
function getData3(){
var defer = $.Deferred();
$.ajax({
url : "p.php",
//async : false,
success: function(data){
defer.resolve(data)
}
});
return defer.promise();
}
$(".btn3").click(function(){
$(".loadingicon").show();
$.when(getData3()).done(function(data){
$(".loadingicon").hide();
alert(data);
});
});
可以看到我在ajax請(qǐng)求中去掉了async:false,也就是說(shuō),這個(gè)請(qǐng)求又是異步的了。另外請(qǐng)注意success函數(shù)中的這一 句:defer.resolve(data),Deferred對(duì)象的resolve方法可傳入一個(gè)參數(shù),任意類型。這個(gè)參數(shù)可以在done方法中拿到, 所以我們異步請(qǐng)求來(lái)的數(shù)據(jù)就可以以這樣的方式來(lái)返回了。
至此,問(wèn)題得到了解決。Deferred對(duì)象如此強(qiáng)大且方便,我們可以好好利用它。
我的全部測(cè)試代碼如下,有意的同學(xué)可以拿去測(cè)一下:
<button class="btn1">async:false</button>
<button class="btn2">setTimeout</button>
<button class="btn3">deferred</button>
<img class="loadingicon" style="position:fixed;left:50%;top:50%;margin-left:-16px;margin-top:-16px;display:none;" src=http://www.update8.com/Web/Jquery/"loading2.gif" alt="正在加載" />
<script>
function getData1(){
var result;
$.ajax({
url : "p.php",
async : false,
success: function(data){
result = data;
}
});
return result;
}
$(".btn1").click(function(){
$(".loadingicon").show();
var data = getData1();
$(".loadingicon").hide();
alert(data);
});
$(".btn2").click(function(){
$(".loadingicon").show();
setTimeout(function(){
$.ajax({
url : "p.php",
async : false,
success: function(data){
$(".loadingicon").hide();
alert(data);
}
});
}, 0);
});
function getData3(){
var defer = $.Deferred();
$.ajax({
url : "p.php",
//async : false,
success: function(data){
defer.resolve(data)
}
});
return defer.promise();
}
$(".btn3").click(function(){
$(".loadingicon").show();
$.when(getData3()).done(function(data){
$(".loadingicon").hide();
alert(data);
});
});</script>
ps:$.ajax的參數(shù)描述
參數(shù) 描述
url 必需。規(guī)定把請(qǐng)求發(fā)送到哪個(gè) URL。
data 可選。映射或字符串值。規(guī)定連同請(qǐng)求發(fā)送到服務(wù)器的數(shù)據(jù)。
success(data, textStatus, jqXHR) 可選。請(qǐng)求成功時(shí)執(zhí)行的回調(diào)函數(shù)。
dataType
可選。規(guī)定預(yù)期的服務(wù)器響應(yīng)的數(shù)據(jù)類型。
默認(rèn)執(zhí)行智能判斷(xml、json、script 或 html)。
相關(guān)文章
jQuery實(shí)現(xiàn)別踩白塊兒網(wǎng)頁(yè)版小游戲
本文主要介紹了jQuery實(shí)現(xiàn)別踩白塊兒網(wǎng)頁(yè)版小游戲的思路分析與代碼。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-01-01
jQuery對(duì)象與DOM對(duì)象轉(zhuǎn)換方法詳解
這篇文章主要介紹了jQuery對(duì)象與DOM對(duì)象的轉(zhuǎn)換方法,結(jié)合實(shí)例形式分析了jQuery對(duì)象及DOM對(duì)象的作用與二者的相互轉(zhuǎn)換技巧,需要的朋友可以參考下2016-05-05
jQuery插件jcrop+Fileapi完美實(shí)現(xiàn)圖片上傳+裁剪+預(yù)覽的代碼分享
這篇文章主要介紹了jQuery插件jcrop+Fileapi完美實(shí)現(xiàn)圖片上傳+裁剪+預(yù)覽的代碼,非常的簡(jiǎn)單實(shí)用,效果也很棒,有需要的小伙伴可以參考下。2015-04-04
jQuery實(shí)現(xiàn)右側(cè)顯示可向左滑動(dòng)展示的深色QQ客服效果代碼
這篇文章主要介紹了jQuery實(shí)現(xiàn)右側(cè)顯示可向左滑動(dòng)展示的深色QQ客服效果代碼,涉及jQuery控制頁(yè)面元素樣式動(dòng)態(tài)變換的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10
ie8模式下click無(wú)反應(yīng)點(diǎn)擊option無(wú)反應(yīng)的解決方法
點(diǎn)擊select里面的option,將其賦值到上面的input,直接用jQuery寫(xiě)的,問(wèn)題是在用IE8打開(kāi)的時(shí)候,點(diǎn)擊option沒(méi)有任何反應(yīng)2014-10-10
jquery實(shí)現(xiàn)checkbox全選全不選的簡(jiǎn)單實(shí)例
本篇文章主要是對(duì)jquery實(shí)現(xiàn)checkbox全選全不選的簡(jiǎn)單實(shí)例進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-12-12
基于jQuery實(shí)現(xiàn)音樂(lè)播放試聽(tīng)列表
這篇文章主要介紹了基于jQuery實(shí)現(xiàn)音樂(lè)播放試聽(tīng)列表的相關(guān)資料,需要的朋友可以參考下2016-04-04
Jquery+WebService 校驗(yàn)賬號(hào)是否已被注冊(cè)的代碼
在Javascirpt代碼中,調(diào)用Jquery的方法$.Ajax(function)實(shí)現(xiàn)Ajax,傳遞賬號(hào)信息給Web服務(wù),Web服務(wù)再調(diào)用數(shù)據(jù)庫(kù)操作類查詢數(shù)據(jù)庫(kù),并返回?cái)?shù)據(jù)給前臺(tái)頁(yè)面。2010-07-07
jquery設(shè)置表單元素為不可用的簡(jiǎn)單代碼
下面小編就為大家?guī)?lái)一篇jquery設(shè)置表單元素為不可用的簡(jiǎn)單代碼。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07

