欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

消除IE stop running this script弹出框

程序员文章站 2022-03-31 14:23:56
...

问题描述和分析:

最近用ExtJS开发的代码在IE8里面跑时, IE跳出了stop running this script的提示, 提示如下:
Stop running this script?
A script on this page is causing your web browser to run slowly. If it continues to run, your computer might become unresponsive.

当然有些提示可能如下:

A script on this page is causing Internet Explorer to run slowly. If it continues to run, your computer may become unresponsive. Do you want to abort the script?

你可以点击"YES"或"NO",点击"NO",IE会继续执行脚本,执行结果跟没有弹出这个框的结果是一样的,点击"YES", IE停止执行脚本,这时候看到状态肯定是不正常的,如果用户都理解原理,并且每次都选择"NO",那么我们就不需要做任何修改了. 但这肯定是不可能的.

咋一看时, 以为肯定是程序的性能慢得IE自己都受不了了,所以跳个框出来,希望用户手动停止, 我的第一反映当然也是这样. 但是奇怪的是我在一台运行速度很快的电脑上, 2秒左右IE也跳出了这个信息, 这就让我困惑不已了,虽然2秒不算短,但是基于web技术的程序代码执行时间超过2秒的应该是可以接受的, 于是google了一下, 看到IE support team 提出的一个解决方案, 参考链接为:http://support.microsoft.com/kb/175500,
里面明确解析如下:

As of Internet Explorer 4.0 and later versions, the time-out is no longer a fixed value based on Windows messages. Internet Explorer now tracks the total number of executed script statements and resets the value each time that a new script execution is started, such as from a timeout or from an event handler, for the current page with the script engine. Internet Explorer displays a "long-running script" dialog box when that value is over a threshold amount. Internet Explorer doesn’t check on each instruction to see if it is over the limit. Periodically the script engine polls Internet Explorer with the number of statements executed and Internet Explorer checks if that is over the limit. Because of this mechanism, it is possible to execute more than the default limit without the dialog if the entire script execution finishes before the script engine polls Internet Explorer.

大致意思是: 从IE4.0的版本开始, 这个'long-running script'的弹出框其实并不是由于你的代码执行的时间超过了一个预先设定的值,而是周期性的检查IE执行的代码总行数是否超过了一个预设的值(这个值可以通过注册表修改),如果超过了就会弹出上面所说的框.

这个设计相当有意思,意味着就算你的代码在1秒钟内执行完成了,但是如果这1秒钟之类执行过的语句行数超过了这个预设的固定值,IE还是会弹出这个stop running script的框,这显然会让我们很困惑. 因为速度这么快,还跳出了这个框,难道要我们的代码不耗时间?

说实话,我看完这段解释后,还是不愿意接受这个观点, 只好自己实验一把了.

实验一,本实验代码会跳出stop running this script框,代码如下:

<html>
	<head>
		<title>IE Long Running Script Issue</title>
	</head>
	<body>
		Statements Execution Number: <span id='outputTxt'>0</span>	
		<script type='text/javascript'>		
			function exceedExectutionStatmentsLimit(){
				var i=0, str='', output=document.getElementById('outputTxt');
				for(; i<5000000; i++){
					str=str+i;
					if(i % 100000 ===0){
						output.innerHTML=i;
					}
				}
				output.innerHTML=i;
			}			
			exceedExectutionStatmentsLimit();
		</script>
	</body>
</html>


用IE打开上面的html页面,会跳出stop...的框,选择yes后,页面输出为
Statements Execution Number: 1700000
这里暂时不解释这个输出,下面会有解释

实验二,本实验IE不会跳出stop running this script框,虽然本代码的执行时间超过了上面的代码

<html>
	<head>
		<title>IE Long Running Script Issue</title>
	</head>
	<body>
		Statements Execution Number: <span id='outputTxt'>0</span>
		<script type='text/javascript'>
			function AvoidLongRunningScriptMessageDisplay(){
				var i=0, str='', output=document.getElementById('outputTxt');
				
				(function () {
				    for (; i < 9000000; i++) {
								str+=i;
				        // Every 100,000 iterations, take a break
				        if (i % 100000 == 0) {
				        		output.innerHTML=i;
				            // Manually increment `i` because we break
				            i++;
				            // Set a timer for the next iteration 
				            window.setTimeout(arguments.callee);
				            break;
				        }
				    }
				})();				
			}
			AvoidLongRunningScriptMessageDisplay();
		</script>
	</body>
</html>


页面输出为Statements Execution Number: 8900000


所以至少得出一个结论,IE官方的解释的确是言行一致,弹出框的跳出只与执行过的代码总行数有关,与其执行耗费的时间无关.


那么接下来有两个问题:
1)代码执行行数的限制是多少;
代码执行行数的限制是多少, 这个值是在注册表里面可修改的,默认是500万行, 当然你可以修改成更大,这样你也能阻止IE跳出stop running this script框,但显然
你只能解决你的IE,其他人的电脑注册表没修改过的话,还是会跳出来的,所以这个方案并不好.关于如何修改本文开头的解决方案3)已经给出了答案.

另外需要注意的是,事实上IE不会每执行一条语句就会检查是否总共执行过的代码行数是否超出了限制,这样显然很耗性能也不明智,IE会周期性的去检查,所以如果你
碰巧在IE来检查时没有超过限制,而不检查时超过了限制,你也不会看到stop script弹出框,当然这就跟中奖一样,看你运气了.

2)代码执行的总行数是怎么计算的;
a) 使用setTimeout调用的函数, 函数里面的代码使用的是一个新的计数器
b) 事件处理函数理的代码使用的也是一个新的计算器
c) 除此之外使用的是同一个计数器,很显然,如果你的代码既没有setTimeout也没有是用
    event handler,那么很容易让计数器超过阀值


另外实验一中点了yes后,为什么输出的是Statements Execution Number: 1700000, 而不是500万呢,那是因为for循环了还有其他的代码,输出的是170万,
但是实际执行的已经有500万了.

如果你还是不相信上面的结论,那么可以用简单的方式,用程序生成500万行左右的javascript语句,看看是否真是500万行会跳出框,我给出了java代码的实现,

有兴趣你可以试一下,但提前预告一些,如果你生成了500万行语句,整个html文件大概有80M大,请做好心理准备,只怕目前你还没有运行过如此大的网页,你的IE
很容易死掉,电脑也可能会变慢,另外下面代码使用的是jdk1.7,

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class GenerateJavascriptCodeForTesting {

	public static void main(String[] args) {
		Path outPut=Paths.get("c:\\tmp\\jsTest.html");
		List<String> content = new ArrayList<String>();
		content.add("<html><head><title>IE Long Running Script Issue</title></head><body><span id='outputTxt'>0</span><script type='text/javascript'>");
		content.add("function exceedExectutionStatmentsLimit(){var i=0, str='', output=document.getElementById('outputTxt');");
		int i=0;
		for(;i<6000000;i++){//try 4000000, 5000000, 6000000 and see whether the stop running script message will be shown up
			content.add("str+="+i+";");
			if(i%100000==0){
				content.add("output.innerHTML="+i+";");
			}
		}
		content.add("output.innerHTML="+i+";}");
		content.add("exceedExectutionStatmentsLimit();");
		content.add("</script></body></html>");
		try {
			Files.write(outPut, content, Charset.forName("UTF-8"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}



解决方案:

首先特别提出一点, 触发事件并执行事件处理函数是一个同步过程,不是异步过程. 可以参考我的另一篇文章:http://darrenzhu.iteye.com/blog/2029822

1. setTimeout
使用setTimeout将大量代码分开执行,每个setTimeout会使用一个新的代码行数计数器,这样你的代码就会减少弹出"Stop running script ..."窗口的机会. 当然很多时候,我们并不会碰到该问题,毕竟500万行代码不是个小数目,不过程序命中的概率比你中500万的彩票显然要容易得多.

2. 通过事件
把你的代码放到事件处理函数里面, 当然事件可以是系统的事件如mouseover, click, 也可以是你自定义和手动触发的. 事件机制为什么能解决这个问题,是因为IE的代码计数器对事件处理函数里面的代码重新计数,已经跟当前控制流的代码计数器分开了.

我更倾向于用事件机制来解决, 因为用setTimeout,你必须指定一个时间,时间设置得不管有多么短,都会有延迟,而事件不会有延迟,当你触发了事件,立马会去执行事件处理函数.

3. 修改注册表, 至于为什么可以通过修改注册表解决这个问题,请看下文的解释
要修改IE4 - IE8的time-out的值,按如下方式操作:
1)运行Regedit32.exe, 找到下面这一项
HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Styles
如果Styles这一项不存在, 你可以创建这一个项

2)创建一个类型为DWORD的键值"MaxScriptStatements", 并且设置它的值为你需要的脚本代码总函数, 如果你不知道设置多少合适, 你可以将其设置成0xFFFFFFFF, 这样就可以避免IE弹出"Stop running Script ..."的框.

默认Styles这一项是不存在的, 这个时候默认的弹出time-out框的代码总函数限制是500万行, 当然是针对IE4 - IE8来说.