項目進行中遇到了同步ajax阻塞ui線程阻塞的問題,原因是執行兩個同步ajax請求為一次完整的方法,因業務需求需要循環執行這個方法,檢查后台返回的數據正確,但是由於ajax請求時間過長,考慮增加遮罩層與loading圖標,
這時遇到該問題,loading圖標 .后面的出圖也是所有方法執行后只出一個圖
代碼類似於這樣:
$(function(){ for(var key in data){//循環執行 $('#mask').css('display','block');//遮罩層 doAjax(); $('#mask').css('display','none');//去除遮罩層 } }); var doAjax = { aAjax:function(){//第一個ajax請求 ajax({ async:false url:url, success:function(){ doAjax.bAjax(); } }); }; bAjax : function(){//第二個ajax請求 ajax({ async:false url:url, success:function(){ doAjax.doHightCharts(); } }); }; doHighCharts:function(){ //出圖 } }
此時為每一個過程考慮兩次js阻礙ui線程的加載,第一次添加遮罩層時,第二次為加載highcharts出圖時.
考慮兩次的原因均因為async:false的原因.由於瀏覽器的渲染(UI)與與js線程是互斥的,在執行js耗時操作時,頁面渲染會被阻塞掉。當我們執行異步ajax的時候沒有問題,但當設置為同步請求時,其他的動作(ajax函數后面的代碼,還有渲染線程)都會停止下來。即使我的DOM操作語句是在發起請求的前一句($('#mask').css('display','block');//遮罩層),這個同步請求也會“迅速”將UI線程阻塞,不給它執行的時間。這就是代碼失效的原因。
在探究這個問題時,了解到更多的知識:(由於項目工期緊,沒能使用各種方法實現)
項目中的這個難題使用最簡單易懂的方式:
setTimeout解決阻塞問題:
var flag = { num:0 } $(function(){ $('#mask').css('display','block');//遮罩層 setTimeout{//首先執行一次 doAjax(),0 } }); var doAjax = { aAjax:function(){//第一個ajax請求 ajax({ async:true,//可以異步 url:url, success:function(){ doAjax.bAjax(); } }); }; bAjax : function(){//第二個ajax請求 ajax({ async:true,//可以異步 url:url, success:function(){ flag.num++;//此時執行避免異步 if (flag.num < data.length) { setTimeout(doAjax.aAjax(), 100); } doAjax.doHightCharts(); if(flag.num==data.length){ $('#mask').css('display','none');//去除遮罩層 } } }); }; doHighCharts:function(){ //出圖 } }
此時簡單來說,setTimeout將方法排列的js執行隊列的最后(哪怕設置第二個參數為0),所以說使用setTimeout是為了確保UI刷新線程不被阻塞.
理解此過程可以根據:
在這里進行簡單的總結:
1.
