在Internet Explorer中禁用长时间运行的脚本消息
我有一个JavaScript函数,其中包含迭代很多次的for循环。
调用该函数后,IE浏览器显示如下信息:
停止运行这个脚本?
此页面上的脚本导致您的Web浏览器运行缓慢。 如果继续运行,您的计算机可能无响应。
我该如何解决这个问题?
无论如何,我可以从IE禁用此消息吗?
当Internet Explorer达到一段JavaScript的最大同步指令数时,将显示此消息。 默认的最大值是5000000条指令,您可以通过编辑registry在一台机器上增加这个数字。
Internet Explorer现在会跟踪已执行的脚本语句的总数,并在每次使用脚本引擎为当前页面启动新脚本执行(例如从超时或事件处理程序)时重置该值。 当该值超过阈值时,Internet Explorer将显示一个“长时间运行的脚本”对话框。
为所有可能查看您的页面的用户解决问题的唯一方法是使用计时器分解循环执行的迭代次数,或者重构代码,以便不需要处理更多的指令。
用计时器打破循环相对简单:
var i=0; (function () { for (; i < 6000000; i++) { /* Normal processing here */ // Every 100,000 iterations, take a break if ( i > 0 && i % 100000 == 0) { // Manually increment `i` because we break i++; // Set a timer for the next iteration window.setTimeout(arguments.callee); break; } } })();
没有响应的脚本对话框显示一些JavaScript线程太长时间太完整。 编辑registry可以工作,但是你必须在所有客户端机器上完成。 你可以使用“recursion闭包”来缓解这个问题。 这只是一个编码结构,在这个结构中,你可以花费很长的时间来循环,并把它改变成一些可以工作的东西,并且跟踪停止的地方,让浏览器继续,直到完成。
图1,将这个Utility Class RepeatingOperation添加到你的javascript文件中。 您将不需要更改此代码:
RepeatingOperation = function(op, yieldEveryIteration) { //keeps count of how many times we have run heavytask() //before we need to temporally check back with the browser. var count = 0; this.step = function() { //Each time we run heavytask(), increment the count. When count //is bigger than the yieldEveryIteration limit, pass control back //to browser and instruct the browser to immediately call op() so //we can pick up where we left off. Repeat until we are done. if (++count >= yieldEveryIteration) { count = 0; //pass control back to the browser, and in 1 millisecond, //have the browser call the op() function. setTimeout(function() { op(); }, 1, []) //The following return statement halts this thread, it gives //the browser a sigh of relief, your long-running javascript //loop has ended (even though technically we havn't yet). //The browser decides there is no need to alarm the user of //an unresponsive javascript process. return; } op(); }; };
图2,下面的代码表示导致“停止运行此脚本”对话框的代码,因为它需要很长时间才能完成:
process10000HeavyTasks = function() { var len = 10000; for (var i = len - 1; i >= 0; i--) { heavytask(); //heavytask() can be run about 20 times before //an 'unresponsive script' dialog appears. //If heavytask() is run more than 20 times in one //javascript thread, the browser informs the user that //an unresponsive script needs to be dealt with. //This is where we need to terminate this long running //thread, instruct the browser not to panic on an unresponsive //script, and tell it to call us right back to pick up //where we left off. } }
图3.下面的代码是图2中有问题代码的修复。注意,for循环被recursion闭包replace,recursion闭包将控制权每10次重复传递给浏览器,
process10000HeavyTasks = function() { var global_i = 10000; //initialize your 'for loop stepper' (i) here. var repeater = new this.RepeatingOperation(function() { heavytask(); if (--global_i >= 0){ //Your for loop conditional goes here. repeater.step(); //while we still have items to process, //run the next iteration of the loop. } else { alert("we are done"); //when this line runs, the for loop is complete. } }, 10); //10 means process 10 heavytask(), then //yield back to the browser, and have the //browser call us right back. repeater.step(); //this command kicks off the recursive closure. };
改编自这个来源:
在我的情况下,在播放video的时候,我需要每次调用一个函数currentTime
的video更新。 所以我使用了video的timeupdate
事件,并且我知道它每秒被激发至less4次(取决于您使用的浏览器,请参阅此处)。 所以我改变它每秒调用一个函数,像这样:
var currentIntTime = 0; var someFunction = function() { currentIntTime++; // Do something here } vidEl.on('timeupdate', function(){ if(parseInt(vidEl.currentTime) > currentIntTime) { someFunction(); } });
这减less了至less1/3
调用someFunc
,它可以帮助您的浏览器正常行为。 它为我做了!
我不能评论以前的答案,因为我没有尝试过。 不过我知道下面的策略适用于我。 这是一个不太优雅,但完成工作。 它也不需要像其他一些方法那样将代码拆分成块。 在我的情况下,这不是一个选项,因为我的代码recursion调用正在循环的逻辑; 也就是说,没有切实可行的方法跳出循环,然后可以通过使用全局variables来保持当前状态,因为这些全局variables可以通过在随后的recursion调用中引用来改变。 所以我需要一种直接的方式,不会让代码损害数据状态完整性。
假设“停止脚本?” 对话框会在执行for()循环执行一段时间(在我的情况下,大约是8-10)之后出现,并且与registry无关,这里是解决方法(对于我来说,无论如何):
var anarray = []; var array_member = null; var counter = 0; // Could also be initialized to the max desired value you want, if // planning on counting downward. function func_a() { // some code // optionally, set 'counter' to some desired value. ... anarray = { populate array with objects to be processed that would have been processed by a for() } // 'anarry' is going to be reduced in size iteratively. Therefore, if you need // to maintain an orig. copy of it, create one, something like 'anarraycopy'. // If you need only a shallow copy, use 'anarraycopy = anarray.slice(0);' // A deep copy, depending on what kind of objects you have in the array, may be // necessary. The strategy for a deep copy will vary and is not discussed here. // If you need merely to record the array's orig. size, set a local or // global var equal to 'anarray.length;', depending on your needs. // - or - // plan to use 'counter' as if it was 'i' in a for(), as in // for(i=0; i < x; i++ {...} ... // Using 50 for example only. Could be 100, etc. Good practice is to pick something // other than 0 due to Javascript engine processing; a 0 value is all but useless // since it takes time for Javascript to do anything. 50 seems to be good value to // use. It could be though that what value to use does depend on how much time it // takes the code in func_c() to execute, so some profiling and knowing what the // most likely deployed user base is going to be using might help. At the same // time, this may make no difference. Not entirely sure myself. Also, // using "'func_b()'" instead of just "func_b()" is critical. I've found that the // callback will not occur unless you have the function in single-quotes. setTimeout('func_b()', 50); // No more code after this. function func_a() is now done. It's important not to // put any more code in after this point since setTimeout() does not act like // Thread.sleep() in Java. Processing just continues, and that is the problem // you're trying to get around. } // func_a() function func_b() { if( anarray.length == 0 ) { // possibly do something here, relevant to your purposes return; } // -or- if( counter == x ) // 'x' is some value you want to go to. It'll likely either // be 0 (when counting down) or the max desired value you // have for x if counting upward. { // possibly do something here, relevant to your purposes return; } array_member = anarray[0]; anarray.splice(0,1); // Reduces 'anarray' by one member, the one at anarray[0]. // The one that was at anarray[1] is now at // anarray[0] so will be used at the next iteration of func_b(). func_c(); setTimeout('func_b()', 50); } // func_b() function func_c() { counter++; // If not using 'anarray'. Possibly you would use // 'counter--' if you set 'counter' to the highest value // desired and are working your way backwards. // Here is where you have the code that would have been executed // in the for() loop. Breaking out of it or doing a 'continue' // equivalent can be done with using 'return;' or canceling // processing entirely can be done by setting a global var // to indicate the process is cancelled, then doing a 'return;', as in // 'bCancelOut = true; return;'. Then in func_b() you would be evaluating // bCancelOut at the top to see if it was true. If so, you'd just exit from // func_b() with a 'return;' } // func_c()