Jquery ajax 同步阻塞所造成的UI執行緒阻塞問題_jquery
最近做一個項目,遇到了一個問題同步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.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html。
於是我用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=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的參數描述
參數 描述
url 必需。規定把請求送到哪個 URL。
data 可選。映射或字串值。規定連同請求發送到伺服器的資料。
success(data, textStatus, jqXHR) 可選。請求成功時執行的回呼函數。
dataType
可選。規定預期的伺服器回應的資料類型。
預設執行智能判斷(xml、json、script 或 html)。

熱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)

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。
