jQuery同步Ajax帶來的UI執行緒阻塞問題的解決
本篇文章主要介紹了jQuery同步Ajax帶來的UI線程阻塞問題及解決辦法,具有一定的參考價值,有興趣的可以了解一下
俗話說不作死就不會死,今天作死了一回,寫了一個比較二逼的函數,遇到了同步Ajax引起的UI線程阻塞問題,在此記錄一下。
事情起因是這樣的,因為頁面上有多個相似的非同步請求動作,本著提高程式碼可重用性的原則,我封裝了一個名為getData的函數,它接收不同參數,只負責取得數據,然後把數據return。基本的邏輯剝離出來是這樣的:
function getData1(){ var result; $.ajax({ url : 'p.php', async : false, success: function(data){ result = data; } }); return result; }
這裡的ajax不能用異步的,否則函數返回時,result還未賦值,會出錯。所以我加了async:false。看起來好像沒什麼問題。我呼叫這個函數可以正常的得到資料。
$('.btn1').click(function(){ var data = getData1(); alert(data); });
接下來,要加另一個功能,由於ajax請求有一定的耗時,所以我需要在發出請求前頁面有個loading效果,即顯示一張「正在載入」的gif圖片,想必大家也都看過。所以我的處理函數變成了這樣:
$('.btn1').click(function(){ $('.loadingicon').show(); var data = getData1(); $('.loadingicon').hide(); alert(data); });
請求之前顯示loading圖片,請求完成後把它隱藏。看起來也沒什麼問題。為了看清效果,我的p.php程式碼sleep了3秒,如下:
<?php sleep(3); echo ('aaaaaa'); ?>
但是我運行的時候問題出現了,我點擊按鈕並未像預想的那樣出現這個loading圖片,頁面什麼反應都沒有。排除良久找到了原因,就在async:false這裡。
瀏覽器的渲染(UI)執行緒和js執行緒是互斥的,在執行js耗時操作時,頁面渲染會被阻塞掉。當我們執行非同步ajax的時候沒有問題,但當設定為同步請求時,其他的動作(ajax函數後面的程式碼,還有渲染線程)都會停止下來。即使我的DOM操作語句是在發起請求的前一句,這個同步請求也會「迅速」將UI執行緒阻塞,不給它執行的時間。這就是程式碼失效的原因。
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的第二個參數設為0,瀏覽器會在一個已設定的最小時間後執行。不管三七二十一先運行起來看看。
結果loading圖片顯示出來了,但是! ! !圖片怎麼不動呢,我明明是一張動態gif圖。這時候我很快就想到了,雖然同步請求延遲執行了,但是它執行期間還是會把UI線程給阻塞。這個阻塞相當牛逼,連gif圖片都不動了,看起來像一張靜態圖片一樣。
結論很明顯,setTimeout治標不治本,相當於把同步請求「稍稍」異步了一下,接下來還是會進入同步的惡夢,阻塞線程。方案失敗。
是時候用Deferred了
jQuery在1.5版本之後,引入了Deferred對象,提供的很方便的廣義異步機制。詳情可參考這篇文章http://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函數中的這句話:defer.resolve(data),Deferred物件的resolve方法可傳入一個參數,任意型別。這個參數可以在done方法中拿到,所以我們非同步請求來的資料就可以用這樣的方式來回傳了。
至此,問題得到了解決。 Deferred物件如此強大且方便,我們可以好好利用它。
我的完整測試程式碼如下,有意的同學可以拿去測一下:
<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有做優化?
上述問題在chrome和IE9中測試結論一致。但是我在Firefox中測試時,同步ajax並未阻塞掉UI線程,也就是說這個問題根本不存在。我用其他程式碼做了測試,在Firefox中js線程確實是會阻塞UI線程,這個沒有疑問。那可能的猜測就是Firefox對同步ajax做了優化,事實到底是什麼,我暫未得知。有高人知道還請指點點。
以上是jQuery同步Ajax帶來的UI執行緒阻塞問題的解決的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

使用PHP和Ajax建置自動完成建議引擎:伺服器端腳本:處理Ajax請求並傳回建議(autocomplete.php)。客戶端腳本:發送Ajax請求並顯示建議(autocomplete.js)。實戰案例:在HTML頁面中包含腳本並指定search-input元素識別碼。

jQuery中如何使用PUT請求方式?在jQuery中,發送PUT請求的方法與發送其他類型的請求類似,但需要注意一些細節和參數設定。 PUT請求通常用於更新資源,例如更新資料庫中的資料或更新伺服器上的檔案。以下是在jQuery中使用PUT請求方式的具體程式碼範例。首先,確保引入了jQuery庫文件,然後可以透過以下方式發送PUT請求:$.ajax({u

標題:jQuery小技巧:快速修改頁面所有a標籤的文字在網頁開發中,我們經常需要對頁面中的元素進行修改和操作。使用jQuery時,有時候需要一次修改頁面中所有a標籤的文字內容,這樣可以節省時間和精力。以下將介紹如何使用jQuery快速修改頁面所有a標籤的文本,同時給出具體的程式碼範例。首先,我們需要引入jQuery庫文件,確保在頁面中引入了以下程式碼:<

使用Ajax從PHP方法取得變數是Web開發中常見的場景,透過Ajax可以實作頁面無需刷新即可動態取得資料。在本文中,將介紹如何使用Ajax從PHP方法中取得變量,並提供具體的程式碼範例。首先,我們需要寫一個PHP檔案來處理Ajax請求,並傳回所需的變數。下面是一個簡單的PHP檔案getData.php的範例程式碼:

為避免執行緒飢餓,可以使用公平鎖確保資源公平分配,或設定執行緒優先權。為解決優先權反轉,可使用優先權繼承,即暫時提高持有資源執行緒的優先權;或使用鎖的提升,即提升需要資源執行緒的優先權。

標題:使用jQuery修改所有a標籤的文字內容jQuery是一款受歡迎的JavaScript庫,被廣泛用於處理DOM操作。在網頁開發中,經常會遇到需要修改頁面上連結標籤(a標籤)的文字內容的需求。本文將介紹如何使用jQuery來實現這個目標,並提供具體的程式碼範例。首先,我們需要在頁面中引入jQuery庫。在HTML檔案中加入以下程式碼:

C++中執行緒終止和取消機制包括:執行緒終止:std::thread::join()阻塞目前執行緒直到目標執行緒完成執行;std::thread::detach()從執行緒管理中分離目標執行緒。執行緒取消:std::thread::request_termination()請求目標執行緒終止執行;std::thread::get_id()取得目標執行緒ID,可與std::terminate()一起使用,立即終止目標執行緒。實戰中,request_termination()允許執行緒決定終止時機,join()確保在主線

Ajax(非同步JavaScript和XML)允許在不重新載入頁面情況下新增動態內容。使用PHP和Ajax,您可以動態載入產品清單:HTML建立一個帶有容器元素的頁面,Ajax請求載入資料後將資料加入到該元素中。 JavaScript使用Ajax透過XMLHttpRequest向伺服器傳送請求,從伺服器取得JSON格式的產品資料。 PHP使用MySQL從資料庫查詢產品數據,並將其編碼為JSON格式。 JavaScript解析JSON數據,並將其顯示在頁面容器中。點選按鈕觸發Ajax請求,載入產品清單。
