俗话说不作死就不会死,今天作死了一回,写了一个比较二逼的函数,遇到了同步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="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:Firefox有做优化?

  上述问题在chrome和IE9中测试结论一致。但是我在Firefox中测试时,同步ajax并未阻塞掉UI线程,也就是说这个问题根本不存在。我用其他代码做了测试,在Firefox中js线程确实是会阻塞UI线程,这个没有疑问。那可能的一个猜测就是Firefox对同步ajax做了优化,事实到底是什么,我暂未得知。有高人知道还请指点。

jQuery同步Ajax带来的UI线程阻塞问题及解决办法的更多相关文章

  1. jQuery同步Ajax带来的UI线程阻塞问题及解决方法

    遇到了同步Ajax引起的UI线程阻塞问题,在此记录一下. 事情起因是这样的,因为页面上有多个相似的异步请求动作,本着提高代码可重用性的原则,我封装了一个名为getData的函数,它接收不同参数,只负责 ...

  2. jQuery同步Ajax带来的UI线程阻塞问题

    一.需求 在调ajax接口的时候因为有时间延迟,想要做一个loading加载的效果,等数据返回时再把loading的效果去掉. 所以我在调ajax的代码块前面加了显示loading效果的代码,ajax ...

  3. Jquery ajax 同步阻塞引起的UI线程阻塞的坑(loading图片显示不出来,layer.load延迟)

    今天想做一个点击地市用ajax重新获取数据刷新页面功能,因为ajax属于耗时操作,想在获取数据且加载页面时显示加载遮罩层,结果发现了ajax的好多坑. 例如如上栗子,我想点击按钮让遮罩层显示,ajax ...

  4. Android Studio学习随笔-UI线程阻塞以及优化

    我们在使用手机的时候,经常会遇到一个问题:先是卡死,然后跳出该程序无响应,是否关闭的提示(当然有可能是我们手机性能太差=.=)这是因为线程的阻塞引起的,在这里我讲述一下UI线程,一般处理程序会在UI线 ...

  5. 第10讲- UI线程阻塞及其优化

    第10讲UI线程阻塞及其优化 .UI 阻塞demo (首先在activity_main.xml中放置两个button,分别命名为button1,button2) //首先设置一个button1用来进行 ...

  6. es6 Object.assign ECMAScript 6 笔记(六) ECMAScript 6 笔记(一) react入门——慕课网笔记 jquery中动态新增的元素节点无法触发事件解决办法 响应式图像 弹窗细节 微信浏览器——返回操作 Float 的那些事 Flex布局 HTML5 data-* 自定义属性 参数传递的四种形式

    es6 Object.assign   目录 一.基本用法 二.用途 1. 为对象添加属性 2. 为对象添加方法 3. 克隆对象 4. 合并多个对象 5. 为属性指定默认值 三.浏览器支持 ES6 O ...

  7. jQuery绑定和解绑点击事件及重复绑定解决办法

    原文地址:http://www.111cn.net/wy/jquery/47597.htm 绑点击事件这个是jquery一个常用的功能,如click,unbind等等这些事件绑定事情,但还有很多朋友不 ...

  8. NGUI发布后UI层看不见的解决办法

    NGUI发布后UI层看不见的解决办法 提示信息:You can'tplace widgets on a layer different than the UIPanel that manages th ...

  9. WPF实战案例-在线程内同步集合数据到UI线程

    有这样一个场景,在vm中,我们为了ui的体验,会异步访问后端接口,获取数据集合,如果这个集合绑定到界面,并且在线程内,怎么处理? 有人讲:this.Dispatcher.Invoke,如果在vm内呢? ...

随机推荐

  1. 188. Best Time to Buy and Sell Stock IV leetcode解题笔记

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  2. 建站阿里云、amh主机面版

    阿里云 Nginx+tomcat7+Mencached负载均衡集群配置 http://blog.csdn.net/zht666/article/details/38515147 apache2.2.1 ...

  3. 新版本MySQL Server 5.7的免安装版本设置

    今天重新配置电脑,安装java开发运行的相关环境,在安装mysql的过程中,遇到了一些问题. 因为在网站上下载的是免安装版本的mysql 5.7 ,所以在安装过程中只需要解压缩zip的压缩包即可. 之 ...

  4. SQL 2005 服务器更计算机名

    select @@ServerNamesp_dropserver '288-BHGKF0MF5G6' sp_addserver '288VIP124','LOCAL' select serverpro ...

  5. Java使用Scanner接收中文并输出时出现乱码

    Java中使用Scanner接收输入的中文并输出时会出现乱码现象,怎么解决此问题呢? 1.方法一 在声明Scanner时添加对应的编码格式就可以了,如下所示: Scanner sc = new Sca ...

  6. 异步请求Ajax

    AJAX:Asynchronous JS And XML,包括HTML.CSS.JS.DOM.XML.JSON等,客户端技术范畴.主要目标:发起异步请求/响应,实现页面内容的局部刷新,提高浏览体验:实 ...

  7. jQuery外链新窗口打开

    对于外链,为了留住用户在本站,我们通常会使用新窗口打开,你可以设置target="_blank".然而手动一个是麻烦,另一个则是有可能会遗漏,本文通过jQuery查询要点击的链接, ...

  8. iOS基础之网络请求相关

    1.AFNetwork二次封装方法一: #import <Foundation/Foundation.h> @interface BeeNetworkManager : NSObject ...

  9. mybatis报invalue types()错误

    错误信息: Cause: org.apache.ibatis.reflection.ReflectionException: Error instantiating class cn.qd.mybat ...

  10. ulipad源码包配置环境及安装

    一.准备下载的安装包: 1.python(我电脑配置的是2.7)下载地址http://pan.baidu.com/s/1qWrGZk4 2.wxpython(我这里是wxpy3.0,配套python2 ...