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对象详解 - 阮一峰的网络日志