Jquery ajax 同步阻塞引起的UI線程阻塞的坑(loading圖片顯示不出來,layer.load延遲)jax重新獲取數據刷新頁面功能,因為ajax屬于耗時操作,想在獲取數據且加載頁面時顯示加載遮罩層,結果發現了ajax的好多坑。
Jquery ajax 同步阻塞引起的UI線程阻塞的坑(loading圖片顯示不出來,layer.load延遲)
ajax請示時,讓遮罩層顯示,ajax加載完畢后遮罩層消失。
因為我想讓loadChart()在賦值操作后執行,但如果async設為true時,往往會先執行loadChart(),之后才會賦值,
所以我只能將ajax設為同步。但同步后無論我怎么點按鈕,遮罩層都不會出來。?
這時:只有:async:true , loading mask層才會出來
?原因就是ajax的async設置為true時,ajax會委托瀏覽器另起一個線程,此線程與js線程和ui線程不沖突,只是在執行完成后再插入js事件環。
而ajax的async設置為false時并沒有啟動單獨的線程,還是在js主線程中執行,所以會與瀏覽器的渲染(UI)線程和js線程是互斥的,在執行js耗時操作時,頁面渲染會被阻塞掉。
當我們執行異步ajax的時候沒有問題,但當設置為同步請求時,其他的動作(ajax函數后面的代碼,還有渲染線程)都會停止下來。即使我的DOM操作語句是在發起請求的前一句,這個同步請求也會“迅速”將UI線程阻塞,不給它執行的時間。這就是代碼失效的原因。?
那么需要使用到:?deferred對象。
?總之,想讓ajax走完再加載頁面,就要使用同步。但是只要同步,ajax就會阻塞ui線程,使得loading顯示不出來。
只有使用了deffer對象和$.when(),既可以ajax設為異步,保證了loading的正常顯示,又可以保證在ajax走完再加載頁面。因為$.when().done()會在deffer.resolve()之前的代碼全部走完后才走done中的代碼。
我改成這樣。由于ajax為同步時點擊切換比較卡。能用異步最好還是用異步,用defferred對象后就可以把async換成true了。$.when()函數只接受defferred對象,所以我們在toGetData中需要先創建對象,再return就解決了。defer.resolve(ret)用于控制ajax何時結束,比如我執行完賦值操作結束ajax,進入.done()中的回調函數,它還可以把數據ret也帶出來使用,這里我沒有用到,這里執行完loadChart()操作后遮罩層消失。所以它能保證deffer.resolve之前的代碼執行完再執行回到函數,async設為true也沒任何影響。
?????這樣就完美解決了因為ajax阻塞線程導致loading層出不來的問題啦。
<script>
? ? var data;
? ? function toGetData() {
? ? ? ? var defer = $.Deferred();
? ? ? ? $.ajax({
? ? ? ? ? ? url: 'xxx',?
? ? ? ? ? ? type: "post", // 請求類型
? ? ? ? ? ? data: {
? ? ? ? ? ? },?
? ? ? ? ? ? dataType: 'json',?
? ? ? ? ? ? async: true, // 是否異步
? ? ? ? ? ? success: function (ret) {
? ? ? ? ? ? ? ? if (ret) {
? ? ? ? ? ? ? ? ? ? data=ret;
? ? ? ? ? ? ? ? ? ? defer.resolve(ret)
?
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? alert("無數據");
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }?
? ? ? ? });
? ? ? ? return defer;
? ? }
? ? $('button').click(function(){
? ? ? ? $(".shadow").show()
? ? ? ? $.when(toGetData()).done(function(ret){
? ? ? ? ? ? loadChart()
? ? ? ? ? ? $(".shodow").hide() ? //所有的ajax的邏輯可以在這個地方進行處理
? ? ? ? });
? ? })
</script>
什么是deferred對象?
開發網站的過程中,我們經常遇到某些耗時很長的javascript操作。其中,既有異步的操作(比如ajax讀取服務器數據),也有同步的操作(比如遍歷一個大型數組),它們都不是立即能得到結果的。
通常的做法是,為它們指定回調函數(callback)。即事先規定,一旦它們運行結束,應該調用哪些函數。
但是,在回調函數方面,jQuery的功能非常弱。為了改變這一點,jQuery開發團隊就設計了deferred對象。
簡單說,deferred對象就是jQuery的回調函數解決方案。在英語中,defer的意思是"延遲",所以deferred對象的含義就是"延遲"到未來某個點再執行。
它解決了如何處理耗時操作的問題,對那些操作提供了更好的控制,以及統一的編程接口。它的主要功能,可以歸結為四點。下面我們通過示例代碼,一步步來學習。
?
$.ajax({
url: "test.html",
success: function(){
alert("哈哈,成功了!");
},error:function(){
alert("出錯啦!");
}});
ajax操作的鏈式寫法?
?
$.ajax("test.html")
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出錯啦!"); });
指定同一操作的多個回調函數?
?
$.ajax("test.html")
.done(function(){ alert("哈哈,成功了!");} )
.fail(function(){ alert("出錯啦!"); } )
.done(function(){ alert("第二個回調函數!");} );
?多個操作指定回調函數
?
$.when($.ajax("test1.html"), $.ajax("test2.html"))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出錯啦!"); });
普通操作的回調函數接口?
var wait = function(){
var tasks = function(){
alert("執行完畢!");
};
setTimeout(tasks,5000);
};
普通回調函數需要改造:
var dtd = $.Deferred(); // 新建一個deferred對象
var wait = function(dtd){
var tasks = function(){
alert("執行完畢!");
dtd.resolve();?// 改變deferred對象的執行狀態
};
setTimeout(tasks,5000);
return dtd;
};
?
$.when(wait())
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出錯啦!"); });
上面這種寫法,還是有問題。那就是dtd是一個全局對象,所以它的執行狀態可以從外部改變。
參考:
jQuery的deferred對象詳解 - 阮一峰的網絡日志?