在Web开发的时候经常会遇到浏览器不响应事件进入假死状态,甚至弹出“脚本运行时间过长“的提示框,如果出现这种情况说明你的脚本已经失控了,必须进行优化。
为什么会出现这种情况呢,我们先来看一下浏览器的内核处理方式:

浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。
了解了浏览器的内核处理方式就不难理解浏览器为什么会进入假死状态了,当一段JS脚本长时间占用着处理机就会挂起浏览器的GUI更新,而后面的事件响应也被排在队列中得不到处理,从而造成了浏览器被锁定进入假死状态。另外JS脚本中进行了DOM操作,一旦JS调用结束就会马上进行一次GUI渲染,然后才开始执行下一个任务,所以JS中大量的DOM操作也会导致事件响应缓慢甚至真正卡死浏览器,如在IE6下一次插入大量的HTML。而如果真的弹出了“脚本运行时间过长“的提示框则说明你的JS脚本肯定有死循环或者进行过深的递归操作了。
Nicholas C. Zakas认为不论什么脚本,在任何时间、任何浏览器上执行都不应该超过100毫秒,否则一定要将脚本分解成若干更小的代码段。那么我们该如何来做呢:
第一步,优化你的循环,循环体中包含太多的操作和循环的次数过多都会导致循环执行时间过长,并直接导致锁死浏览器。如果循环之后没有其他操作,每次循环只处理一个数值,而且不依赖于上一次循环的结果则可以对循环进行拆解,看下面的chunk的函数:
1
|
functionchunk(array,
process, context) {
|
2
|
setTimeout(function()
{
|
3
|
varitem
= array.shift();
|
4
|
process.call(context,
item);
|
5
|
if(array.length
> 0) {
|
6
|
setTimeout(arguments.callee,
100);
|
7
|
}),
100);
|
8
|
}
|
chunk()函数的用途就是将一个数组分成小块处理,它接受三个参数:要处理的数组,处理函数以及可选的上下文环境。每次函数都会将数组中第一个对象取出交给process函数处理,如果数组中还有对象没有被处理则启动下一个timer,直到数组处理完。这样可保证脚本不会长时间占用处理机,使浏览器出一个高响应的流畅状态。
其实在我看来,借助JS强大的闭包机制任何循环都是可拆分的,下面的版本增加了callback机制,使可再循环处理完毕之后进行其他的操作。
01
|
functionchunk(array,process,cbfun){
|
02
|
vari=0,len
= array.length;//这里要注意在执行过程中数组最好是不变的
|
03
|
setTimeout(function(){
|
04
|
process(
array[i] , i++ );//循环体要做的操作
|
05
|
if(
i < len ){
|
06
|
setTimeout(arguments.callee,100)
|
07
|
}else{
|
08
|
cbfun()//循环结束之后要做的操作
|
09
|
}
|
10
|
}
|
11
|
}
|
第二步,优化你的函数,如果函数体内有太多不相干但又要一起执行的操作则可以进行拆分,考虑下面的函数:
1
|
functiondosomething(){
|
2
|
dosomething1();
|
3
|
dosomething2();
|
4
|
}
|
dosomething1和dosomething2互不相干,执行没有先后次序,可用前面提到的chunk函数进行拆分:
1
|
functiondosomething(){
|
2
|
chunk([dosomething1,dosomething2],function(item){item();})
|
3
|
}
|
或者直接交给浏览器去调度
1
|
functiondosome(){
|
2
|
setTimeout(dosomething1,0);
|
3
|
setTimeout(dosomething2,0);
|
4
|
}
|
第三步,优化递归操作,函数递归虽然简单直接但是过深的递归操作不但影响性能而且稍不注意就会导致浏览器弹出脚本失控对话框,必须小心处理。
看以下斐波那契数列的递归算法:
1
|
functionfibonacci(n)
{
|
2
|
returnn
< 2 ? n: fibonacci(n - 1) + fibonacci(n - 2);
|
3
|
};
|
fibonacci(40)这条语句将重复调用自身331160280次,在浏览器中执行必然导致脚本失控,而采用下面的算法则只需要调用40次
01
|
fibonacci
=function(n){
|
02
|
varmemo
= {0:0,1:0};//计算结果缓存
|
03
|
varshell
=function(n){
|
04
|
varresult
= memo[n];
|
05
|
if(typeofresult
!='number')//如果值没有被计算则进行计算
|
06
|
memo[n]
= shell(n-1) + shell(n -2)
|
07
|
returnmemo[n];
|
08
|
}
|
09
|
returnshell(n);
|
10
|
}
|
这项技术被称为memoization,他的原理很简单就是同样的结果你没必要计算两次。另一种消除递归的办法就是利用迭代,递归和迭代经常会被作为互相弥补的方法。
第四步,减少DOM操作,DOM操作的代价是相当昂贵的,大多数DOM操作都会触发浏览器的回流(reflow)操作。例如添加删除节点,修改元素样式,获取需要经过计算的元素样式等。我们要做的就是尽量少的触发回流操作。
1
|
el.style.width
='300px'el.style.height
='300px'el.style.backgroundColor
='red'
|
上面的操作会触发浏览器的三次回流操作,再看下面的方式:
1
|
el.className
='newStyle'
|
通过设置改元素的className一次设置多个样式属性,将样式写再CSS文件中,只触发一次回流,达到了同样是效果而且效率更高。因为浏览器最擅长的就是根据class设置样式。
还有很多可以减少DOM操作的方法,在此就不多说了,但是一个基本的原则就是让浏览器去做它自己擅长的事情,例如通过class来改变元素的属性。
相信经过上面的优化的过程必定可以大大提高用户体验,不会出现浏览器被锁死和弹出脚本失控的对话框,使你的浏览器从繁重的任务中解放出来。需要指出的是上面这些优化并不是必须的,只有当一段脚本的执行时间真的影响到了用户体验才需要进行。虽然它们让用户觉得脚本的执行变快了,但其实完成同一个操作的时间可能被延长了,这些技术只是让浏览器处于一个快速响应的状态,使用户浏览更流畅。
最后送一句忠告:过早优化是万恶之源。
来源:网络
最近用chrome浏览器上传图片时,总是卡死,不管是自己的程序,还是别人家的网站,都卡死。如图: 只有卸载重装之后才可以上传,但是电脑关机重启后,chrome浏览器必须重装才行,找了很久,没找到解决方法,始终重装浏览器总是不行的。 晚饭后随便翻翻,发现了一种解决方法: 说只搜狗输入法的锅,切换输入法后就行,也没有报太大的希望,就抱着试试的心态测试了下,还真的解决了。。。。。。。。 此处mark下,...
转自:https://jingyan.baidu.com/article/e8cdb32b3aabef37042bad67.html Chrome浏览器上传图片文件卡死的解决方法 听语音 | 浏览:4098 | 更新:2015-07-31 04:27 | 标签:Chrome 1 2 3 4 5 6 7 分步阅读 Chrome浏览器本来非常好用,但是可能有的人在更新版本或重...
现象 使用谷歌浏览器正常上传头像或者上传文件,整个浏览器卡死不动了,只能在任务管理器杀死进程。 导致原因 网上找了一些文章,有说是谷歌浏览器插件,有说是输入法。 解决我的问题的是改一下输入法就ok了。 但是这个问题我老早就改过了。好使了一阵子,最近又重现卡死的状态了,然后看了一下输入法也是修改后。 于是就Ctrl+Shift切换一下输入法,切换到abc或者其他系统自带的...
本人比较习惯使用Chrome浏览器作为网页的测试工具,发现在我想要弹出警告框或确认框时,浏览器不但没有正常弹出窗口框,而且还会被卡死。 在网上找了许多方法还是没有用,最终还是在一个论坛的回复中成功找到解决方法。为了方便有该问题困扰的同志们可以成功解决,我觉得还是分享一下好了。 废话少说,直接开题: 打开设置 打开“显示高级设置” 来到“系统”这一项 ...
前段时间一直想学学微信中的小程序是如何制作的,昨天终于有时间就尝试了一下。话不多说,以免跑偏。。。。 1.微信小程序开发需要先在微信公众平台(https://mp.weixin.qq.com)上注册一个自己的公众号。 2.在公众平台上注册成功后,在公众号左侧有一个小程序栏目。点击小程序管理,在左侧就会出现两个选项,在第一次使用小程序当然是没有小程序账号的,点击右侧注册小...
参考链接: Win10+VS2015环境下安装编译PCL1.8.1和VTK8.0.0(踩坑大全)https://blog.csdn.net/weixin_39871164/article/details/102879962 1.vs2017编译安装VTK (PCL点云显示是基于VTK做的) - PCL191匹配的VTK版本是VTK8.1(PCL191\3rdParty\VTK\include\vt...
一、union注入 基于union的信息获取: union联合查询:可以通过联合查询来查询指定的数据 用法举例: select username,password from user where id =1 union select 字段1,字段2 from 表名 先登入PHPmyadmin 进入pikachu的数据库中 得到此结果 上述为一般查询 union查询: 除了通过遍历查询数据库内容外,...
String常用的几个方法介绍 首先在讲之前我想说几句题外话,关于string的常用方法我查了很久csdn,我发现要不就是一大坨,还有一些乱七八糟的拓展,看的你一脸懵逼,要不就是简洁的很,该写的你不写出来,所以搞得你很难受。但是在你看了我这篇文章以后我觉得你可能会缓解很多,那就开始吧。 String的方法是有很多的,这里我只列举一些我常用的,喷子不要喷。 直接上代码先。 方法一:charAt: 很...
本次得分为:15.00/15.00, 本次测试的提交时间为:2020-04-24, 如果你认为本次测试成绩不理想,你可以选择再做一次。 【第5章 测试(下)】 1单选(1分) 下图可以表示哪种控制方式的CPU工作效率?选A 得分/总分 A. DMA方式 1.00/1.00 B.无正确答案 C.程序中断方式 D.程序查询方式 2单选(1分) 在程序中断方式下,中断响应发生在__B_ 得分/总分 A....
一、题目描述 判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。 示例 1: 输入: 121 输出: true 示例 2: 输入: -121 输出: false 解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。 示例 3: 输入: 10 输出: false 解释: 从右向左读, 为 01 。因此它不是一个回文数。 进...
I am using a MultiInput control to which I am adding Tokens based on input from the user. For this I have used addValidator function to add Token with 'key' and 'text'. I am looking to add operator (l...
I am trying to make a request in a document with mongodb. In this document I'm looking to get just the id in friend, not all of the document just 1 id in friend with where i try this : It's not workin...
How can i use Rspec Expectations in Page-object classes. I need to assert elements. Currently i am using xpath, other locators to check element existence. I know using it is step definitions. But i ne...
I'm writing network application in c++ and I want to enable making plugins, but I don't know what to do, to protect my application from errors like segfault. For example: I have interface: And someone...
I've an anchor in an Asp.Net user control. I've wired some code to the onclick event. For some reason in Internet Explorer 10, the event is not firing. The markup is structured as where myFunction is ...