JavaScript-如何防止由于脚本引起的浏览器假死

JavaScript-如何防止由于脚本引起的浏览器假死

想挽留 发布于 2016-11-18 字数 272 浏览 1207 回复 3

在Web开发的时候经常会遇到浏览器不响应事件进入假死状态,
甚至弹出“脚本运行时间过长“的提示框,
如果出现这种情况说明你的脚本已经失控了。
求解决方案:
说明:道理就不要和我说了,我也资料查过,请给出您的实用代码最好,谢谢!

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浮生未歇 2017-05-24 3 楼

由于JavaScript引擎是基于事件驱动单线程执行的。所以只要当一个事件阻塞了当前执行,其它的都需要等待,所以引起浏览器假死的情况有很多,比如:
1、大量的for等造成死循环。
2、由于代码原因,造成IE内存泄露
3、setTimeOut或者setInterval造成线程挂起
4、对DOM进行大量的操作

以上说的是造成假死的大致原因,至于你说的解决方案,要看具体的代码,是什么原因造成的。对症才能下药。

晚风撩人 2017-02-09 2 楼

尊重版权,看人家总结的挺好:优化js脚本设计,防止浏览器假死
第一步,优化你的循环,循环体中包含太多的操作和循环的次数过多都会导致循环执行时间过长,并直接导致锁死浏览器。如果循环之后没有其他操作,每次循环只处理一个数值,而且不依赖于上一次循环的结果则可以对循环进行拆解,看下面的chunk的函数:

function chunk(array, process, context) {
setTimeout(function() {
var item = array.shift();
process.call(context, item);
if (array.length > 0) {
setTimeout(arguments.callee, 100);
}), 100);
}

chunk()函数的用途就是将一个数组分成小块处理,它接受三个参数:要处理的数组,处理函数以及可选的上下文环境。每次函数都会将数组中第一个对象取出交给process函数处理,如果数组中还有对象没有被处理则启动下一个timer,直到数组处理完。这样可保证脚本不会长时间占用处理机,使浏览器出一个高响应的流畅状态。

其实在我看来,借助JS强大的闭包机制任何循环都是可拆分的,下面的版本增加了callback机制,使可再循环处理完毕之后进行其他的操作。

function chunk(array,process,cbfun){
var i=0,len = array.length; //这里要注意在执行过程中数组最好是不变的
setTimeout(function(){
process( array[i] , i++ ); //循环体要做的操作
if( i < len ){
setTimeout(arguments.callee,100)
}else{
cbfun() //循环结束之后要做的操作
}
}
}

第二步,优化你的函数,如果函数体内有太多不相干但又要一起执行的操作则可以进行拆分,考虑下面的函数:

 function dosomething(){
dosomething1();
dosomething2();
}

dosomething1和dosomething2互不相干,执行没有先后次序,可用前面提到的chunk函数进行拆分:

 function dosomething(){
chunk([dosomething1,dosomething2],function(item){item();})
}

或者直接交给浏览器去调度

 function dosome(){
setTimeout(dosomething1,0);
setTimeout(dosomething2,0);
}

第三步,优化递归操作,函数递归虽然简单直接但是过深的递归操作不但影响性能而且稍不注意就会导致浏览器弹出脚本失控对话框,必须小心处理。

看以下斐波那契数列的递归算法:

 function fibonacci(n) {
return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2);
};

fibonacci(40)这条语句将重复调用自身331160280次,在浏览器中执行必然导致脚本失控,而采用下面的算法则只需要调用40次

 fibonacci = function(n){
var memo = {0:0,1:0}; //计算结果缓存
var shell = function(n){
var result = memo[n];
if( typeof result != 'number' ) //如果值没有被计算则进行计算
memo[n] = shell(n-1) + shell(n -2)
return memo[n];
}
return shell(n);
}

这项技术被称为memoization,他的原理很简单就是同样的结果你没必要计算两次。另一种消除递归的办法就是利用迭代,递归和迭代经常会被作为互相弥补的方法。

第四步,减少DOM操作,DOM操作的代价是相当昂贵的,大多数DOM操作都会触发浏览器的回流(reflow)操作。例如添加删除节点,修改元素样式,获取需要经过计算的元素样式等。我们要做的就是尽量少的触发回流操作。

 el.style.width = '300px' el.style.height = '300px' el.style.backgroundColor = 'red'

上面的操作会触发浏览器的三次回流操作,再看下面的方式:

 el.className = 'newStyle'

通过设置改元素的className一次设置多个样式属性,将样式写再CSS文件中,只触发一次回流,达到了同样是效果而且效率更高。因为浏览器最擅长的就是根据class设置样式。

还有很多可以减少DOM操作的方法,在此就不多说了,但是一个基本的原则就是让浏览器去做它自己擅长的事情,例如通过class来改变元素的属性。

相信经过上面的优化的过程必定可以大大提高用户体验,不会出现浏览器被锁死和弹出脚本失控的对话框,使你的浏览器从繁重的任务中解放出来。需要指出的是上面这些优化并不是必须的,只有当一段脚本的执行时间真的影响到了用户体验才需要进行。虽然它们让用户觉得脚本的执行变快了,但其实完成同一个操作的时间可能被延长了,这些技术只是让浏览器处于一个快速响应的状态,使用户浏览更流畅。

归属感 2016-12-03 1 楼

1,JavaScript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来然后加以处理,浏览器无论再什么时候都只有一个JS线程在运行JS程序。

2,GUI 渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

3,事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。

4,采用html5的新特性,webworker.