功能需求
编写点击按钮,弹出loading遮罩层提示正在加载中…,同时查询后台信息,拼接数据后渲染到列表时,为了保障渲染时,列表中的数据已经拼接完成。所以在for循环中使用同步ajax进行请求,在success方法中进行拼接。
function showPrintJobList(){var loadingTip = $('#loading');loadingTip.show();for(var i = 0;i < rwid.length;i++){$.ajax({url: xxxxx,async: false, //默认异步,为保证数据完整和顺序使用同步请求type: 'POST',data:{xxx: xxx},dataType: 'json',success: function(result) {//.....循环拼接结果,弹窗显示列表:$('#rwlist').show();},error: function(err){console.log(err);}});}loadingTip.hide();$('#rwlist').show();}
问题信息
点击按钮后,遮罩层没有弹出,而是等待表单渲染完成后,看到一闪而过的遮罩层。
问题原因async: false
ajax同步请求在运行的时候,浏览器的UI、Dom元素渲染会被阻塞。所以当点击事件触发showPrintJobList函数时,loading.show()是有被加载到浏览器的js运行线程中,但是运行到ajax同步请求时,会将loading.show()阻塞执行,等到同步ajax请求完了,才会运行。然后同步ajax已经请求完了,然后直接被隐藏,然后显示列表。所以会看到一闪而过的loading遮罩层
解决方法
一、使用async wait关键字
二、使用JQuery的Deferred对象和$.when().done().fail()
async/await关键字
标识所修饰的函数存在异步代码,函数内部可用await关键字修饰异步过程变为同步。函数内部也可以全部为同步代码。非阻塞:async所标识的函数内存在异步代码,会等待异步代码执行完,但是async函数本身会马上返回,不会阻塞当前js工作主线程,可以简单认为,async函数工作在主线程,同步执行,不会阻塞界面渲染,async函数内部由await关键字修饰的异步过程,工作在相应的协程上,会阻塞等待异步任务的完成再返回;代码修改如下:
async function showPrintJobList(){var loadingTip = $('#loading');loadingTip.show();for(var i = 0;i < rwid.length;i++){await $.ajax({//使用await关键字修饰异步请求,保证数据顺序完整url: xxxxx,async: true, //有await关键字,ajax请求修改为异步type: 'POST',data:{xxx: xxx},dataType: 'json',success: function(result) {//.....循环拼接结果,弹窗显示列表:$('#rwlist').show();},error: function(err){console.log(err);}});}loadingTip.hide();$('#rwlist').show();}
或者使用JQuery的Deferred对象和$.when().done().fail()使异步请求同步执行,同时不阻塞主线程。
var dataList = [];function showPrintJobList(){var defer = $.Deferred();for(var i = 0;i < rwid.length;i++){$.ajax({url: xxxxx,async: true, //使用Deferred对象,ajax请求修改为异步type: 'POST',data:{xxx: xxx},dataType: 'json',success: function(result) {//dataList保存结果dataList.push(result);defer.resolve(dataList);},error: function(err){defer.reject(err);}});}return defer;}$('button#showList').click(function(){var loadingTip = $('#loading');loadingTip.show();$.when(showPrintJobList()).done(function(data){//循环拼接data中的list中的数据loadingTip.hide();$('#rwlist').show();}).fail(function(){console.log(err);});}
原理解析
浏览器中一般会运行UI线程、JS线程以及事件触发线程。
在上述上述代码中,loading.show()方法涉及到dom树的更新,所以被加载到了UI线程执行任务中。但是后面的ajax同步请求,因为不是异步的所以浏览器不会重新新增一个线程进行请求,而是直接放在js线程中阻塞其他线程执行。所以UI线程不能执行loading.show()方法。
如果觉得《函数内的ajax同步请求导致遮罩层失效 或者导致loading正在加载提示失效问题》对你有帮助,请点赞、收藏,并留下你的观点哦!