詳解jQuery同步Ajax帶來的UI線程阻塞問題及解決辦法
俗話說不作死就不會(huì)死,今天作死了一回,寫了一個(gè)比較二逼的函數(shù),遇到了同步Ajax引起的UI線程阻塞問題,在此記錄一下。
事情起因是這樣的,因?yàn)轫撁嫔嫌卸鄠€(gè)相似的異步請(qǐng)求動(dòng)作,本著提高代碼可重用性的原則,我封裝了一個(gè)名為getData的函數(shù),它接收不同參數(shù),只負(fù)責(zé)獲取數(shù)據(jù),然后把數(shù)據(jù)return?;镜倪壿媱冸x出來是這樣的:
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??雌饋砗孟駴]什么問題。我調(diào)用這個(gè)函數(shù)可以正常的得到數(shù)據(jù)。
$('.btn1').click(function(){
var data = getData1();
alert(data);
});
接下來,要加另外一個(gè)功能,由于ajax請(qǐng)求有一定的耗時(shí),所以我需要在發(fā)出請(qǐng)求前頁面有個(gè)loading效果,即顯示一張“正在加載”的gif圖片,想必大家也都見過。所以我的處理函數(shù)就變成了這樣:
$('.btn1').click(function(){
$('.loadingicon').show();
var data = getData1();
$('.loadingicon').hide();
alert(data);
});
請(qǐng)求之前顯示loading圖片,請(qǐng)求完成后把它隱藏。看起來也沒什么問題。為了看清效果,我的p.php代碼sleep了3秒,如下:
<?php
sleep(3);
echo ('aaaaaa');
?>
但是我運(yùn)行的時(shí)候問題出現(xiàn)了,我點(diǎn)擊按鈕并未像預(yù)想的那樣出現(xiàn)這個(gè)loading圖片,頁面什么反應(yīng)也沒有。排除良久找到了原因,就在async:false這里。
瀏覽器的渲染(UI)線程和js線程是互斥的,在執(zhí)行js耗時(shí)操作時(shí),頁面渲染會(huì)被阻塞掉。當(dāng)我們執(zhí)行異步ajax的時(shí)候沒有問題,但當(dāng)設(shè)置為同步請(qǐng)求時(shí),其他的動(dòng)作(ajax函數(shù)后面的代碼,還有渲染線程)都會(huì)停止下來。即使我的DOM操作語句是在發(fā)起請(qǐng)求的前一句,這個(gè)同步請(qǐng)求也會(huì)“迅速”將UI線程阻塞,不給它執(zhí)行的時(shí)間。這就是代碼失效的原因。
setTimeout解決阻塞問題
既然明白了問題在哪里,我們就來針對(duì)性想辦法。為了不讓同步ajax請(qǐng)求阻塞線程,我想到了setTimeout,把請(qǐng)求的代碼放到sestTimeout中,讓瀏覽器重啟一個(gè)線程來操作,不就解決問題了嗎?于是乎,我的代碼就變成了這樣:
$('.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)行起來看看。
結(jié)果loading圖片顯示出來了,但是!??!圖片怎么不動(dòng)呢,我明明是一張動(dòng)態(tài)gif圖。這個(gè)時(shí)候我很快就想到了,雖然同步請(qǐng)求延遲執(zhí)行了,但是它執(zhí)行期間還是會(huì)把UI線程給阻塞。這個(gè)阻塞相當(dāng)牛逼,連gif圖片都不動(dòng)了,看起來像一張靜態(tài)圖片一樣。
結(jié)論很明顯,setTimeout治標(biāo)不治本,相當(dāng)于把同步請(qǐng)求“稍稍”異步了一下,接下來還是會(huì)進(jìn)入同步的噩夢(mèng),阻塞線程。方案失敗。
是時(shí)候用Deferred了
jQuery在1.5版本之后,引入了Deferred對(duì)象,提供的很方便的廣義異步機(jī)制。詳情可參看這篇文章http://www.dhdzp.com/article/54762.htm。
于是我用Deferred對(duì)象改寫了代碼,如下:
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,也就是說,這個(gè)請(qǐng)求又是異步的了。另外請(qǐng)注意success函數(shù)中的這一句:defer.resolve(data),Deferred對(duì)象的resolve方法可傳入一個(gè)參數(shù),任意類型。這個(gè)參數(shù)可以在done方法中拿到,所以我們異步請(qǐng)求來的數(shù)據(jù)就可以以這樣的方式來返回了。
至此,問題得到了解決。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="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:Firefox有做優(yōu)化?
上述問題在chrome和IE9中測(cè)試結(jié)論一致。但是我在Firefox中測(cè)試時(shí),同步ajax并未阻塞掉UI線程,也就是說這個(gè)問題根本不存在。我用其他代碼做了測(cè)試,在Firefox中js線程確實(shí)是會(huì)阻塞UI線程,這個(gè)沒有疑問。那可能的一個(gè)猜測(cè)就是Firefox對(duì)同步ajax做了優(yōu)化,事實(shí)到底是什么,我暫未得知。有高人知道還請(qǐng)指點(diǎn)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JQuery中使用ajax傳輸超大數(shù)據(jù)的解決方法
這篇文章主要介紹了JQuery中使用ajax傳輸超大數(shù)據(jù)的解決方法,也就是比較多的數(shù)據(jù),超過max_upload_size等設(shè)置,本文方法在chrome瀏覽器下測(cè)試通過,需要的朋友可以參考下2014-07-07
jQuery實(shí)現(xiàn)獲取綁定自定義事件元素的方法
這篇文章主要介紹了jQuery實(shí)現(xiàn)獲取綁定自定義事件元素的方法,涉及jQuery方法重載、事件綁定及元素操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-12-12
jQuery擴(kuò)展_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了jQuery擴(kuò)展,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07
jQuery選擇器簡(jiǎn)明總結(jié)(含用法實(shí)例,一目了然)
jQuery 選擇器一直是 jquery 最神秘,最強(qiáng)大的一部分,當(dāng)然也是整個(gè) jquery 里面非?;A(chǔ)的2014-04-04
jQuery學(xué)習(xí)筆記(3)--用jquery(插件)實(shí)現(xiàn)多選項(xiàng)卡功能
多選項(xiàng)卡功能的網(wǎng)站有很多,本人在那么多的類似插件中,目前碰到這個(gè)比較好,花了點(diǎn)時(shí)間調(diào)試出來了與大家分享,感興趣的朋友可以參考下哈希望對(duì)你有所幫助2013-04-04
jquery教程ajax請(qǐng)求json數(shù)據(jù)示例
這篇文章主要介紹了jquery獲取JSON對(duì)象中數(shù)據(jù)的示例,代碼中有注釋,大家參考使用吧2014-01-01
jquery獲取transform里的值實(shí)現(xiàn)方法
下面小編就為大家分享一篇jquery獲取transform里的值實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12
html文件中jquery與velocity變量中的$沖突的解決方法
在使用velocity模版引擎的環(huán)境下,使用jquery時(shí)其中$與velocity變量中的$沖突,下面有個(gè)不錯(cuò)的解決方法,感興趣的朋友可以參考下2013-11-11
跟著Jquery API學(xué)Jquery之一 選擇器
Jquery 選擇器是最基本的操作了,當(dāng)我們用原生的javascript的時(shí)候,我們?yōu)榱诉x擇一個(gè)對(duì)象不得不花費(fèi)九頭二虎之力2010-04-04

