這篇文章主要介紹如何解決jQuery同步Ajax帶來的UI線程阻塞問題,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
長洲網站建設公司創(chuàng)新互聯(lián)建站,長洲網站設計制作,有大型網站制作公司豐富經驗。已為長洲上1000+提供企業(yè)網站建設服務。企業(yè)網站搭建\外貿網站建設要多少錢,請找那個售后服務好的長洲做網站的公司定做!
事情起因是這樣的,因為頁面上有多個相似的異步請求動作,本著提高代碼可重用性的原則,我封裝了一個名為getData的函數(shù),它接收不同參數(shù),只負責獲取數(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ù)返回時,result還未賦值,會出錯。所以我加了async:false。看起來好像沒什么問題。我調用這個函數(shù)可以正常的得到數(shù)據(jù)。
$('.btn1').click(function(){ var data = getData1(); alert(data); });
接下來,要加另外一個功能,由于ajax請求有一定的耗時,所以我需要在發(fā)出請求前頁面有個loading效果,即顯示一張“正在加載”的gif圖片,想必大家也都見過。所以我的處理函數(shù)就變成了這樣:
$('.btn1').click(function(){ $('.loadingicon').show(); var data = getData1(); $('.loadingicon').hide(); alert(data); });
請求之前顯示loading圖片,請求完成后把它隱藏??雌饋硪矝]什么問題。為了看清效果,我的p.php代碼sleep了3秒,如下:
但是我運行的時候問題出現(xiàn)了,我點擊按鈕并未像預想的那樣出現(xiàn)這個loading圖片,頁面什么反應也沒有。排除良久找到了原因,就在async:false這里。
瀏覽器的渲染(UI)線程和js線程是互斥的,在執(zhí)行js耗時操作時,頁面渲染會被阻塞掉。當我們執(zhí)行異步ajax的時候沒有問題,但當設置為同步請求時,其他的動作(ajax函數(shù)后面的代碼,還有渲染線程)都會停止下來。即使我的DOM操作語句是在發(fā)起請求的前一句,這個同步請求也會“迅速”將UI線程阻塞,不給它執(zhí)行的時間。這就是代碼失效的原因。
setTimeout解決阻塞問題
既然明白了問題在哪里,我們就來針對性想辦法。為了不讓同步ajax請求阻塞線程,我想到了setTimeout,把請求的代碼放到sestTimeout中,讓瀏覽器重啟一個線程來操作,不就解決問題了嗎?于是乎,我的代碼就變成了這樣:
$('.btn2').click(function(){ $('.loadingicon').show(); setTimeout(function(){ $.ajax({ url : 'p.php', async : false, success: function(data){ $('.loadingicon').hide(); alert(data); } }); }, 0); });
setTimeout的第二個參數(shù)設為0,瀏覽器會在一個已設的最小時間后執(zhí)行。不管三七二十一先運行起來看看。
結果loading圖片顯示出來了,但是!??!圖片怎么不動呢,我明明是一張動態(tài)gif圖。這個時候我很快就想到了,雖然同步請求延遲執(zhí)行了,但是它執(zhí)行期間還是會把UI線程給阻塞。這個阻塞相當牛逼,連gif圖片都不動了,看起來像一張靜態(tài)圖片一樣。
結論很明顯,setTimeout治標不治本,相當于把同步請求“稍稍”異步了一下,接下來還是會進入同步的噩夢,阻塞線程。方案失敗。
是時候用Deferred了
jQuery在1.5版本之后,引入了Deferred對象,提供的很方便的廣義異步機制。詳情可參看這篇文章https://www.jb51.net/article/54762.htm。
于是我用Deferred對象改寫了代碼,如下:
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請求中去掉了async:false,也就是說,這個請求又是異步的了。另外請注意success函數(shù)中的這一句:defer.resolve(data),Deferred對象的resolve方法可傳入一個參數(shù),任意類型。這個參數(shù)可以在done方法中拿到,所以我們異步請求來的數(shù)據(jù)就可以以這樣的方式來返回了。
至此,問題得到了解決。Deferred對象如此強大且方便,我們可以好好利用它。
我的全部測試代碼如下,有意的同學可以拿去測一下:
以上是“如何解決jQuery同步Ajax帶來的UI線程阻塞問題”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道!