5

Internet explorer 8 (which I need to support, non-negotiable unfortunately) displays the following warning once 5,000,000 "executed script statements" have occurred in one synchronous script execution (e.g. from a timeout or from an event handler).

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.

I'm trying to optimise some complex, heavy-duty code to avoid this error message. I've already followed the standard advise, which is to where possible break the code into seperate asynchronous chunks using things like setTimeout(). I can't do any more of this without creating race conditions, so I'm looking to streamline all code that happens many times e.g. within large for loops.

To do this, I want to understand what exactly IE counts as an "executed script statement", so I will know which optimisations within my loops will make the most difference and which won't.

I've looked for standard definitions of "executed script statements", without success - most times "script statement" is used, it appears to refer to the contents of a html <script> tag which is clearly a different sense of the term. The definition Statement (computer science) is helpful but leaves some ambiguity about how they are counted (see examples below).

Here's what Microsoft have to say on the matter (I've added paragraph breaks for readability):

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.

To give some simple examples that seem ambiguous to me:

  1. Would var a = 1, b = 2, c = 3; count as one "executed script statement" and var a = 1; var b = 2; var c = 3; count as three? Or would both be three?
  2. Would if( someFunction() ){} (not counting statements within someFunction() be one statement, or two (a call plus a conditional)?
  3. Is if(a){}else{} one conditional statement or two? If one, would if(a){}else if(b){} be two?
  4. Is if(a==b||(c&&a==c&&c==d)){} one, two, three, four, five statements (or more?)? I'm aware that anything like if(a){} calls a Javascript function to convert to boolean - would this add additional statements on top of the comparisons themselves?
  5. Would var value = someFunction(); if( value ){} be three since it adds an assignment, or would the function call be counted as part of the assignment statement?
  6. What about chaining? For example, if jQuery is used, then (not counting the executed script statements within each function) is the line $(selector).show().css(obj).appendTo($el); one "executed script statement", or four? I'd imagine it would be four "call" statements.
  7. Presumably, var $someEl = $(selector).show().css(obj).appendTo($el); would increase this to five statements - four calls plus an assignment? (IE wouldn't just count it as one assignment statement and move on?)

Naturally these simple examples above are small fry - I'm trying to "know the enemy" here to be able to judge how best to optimise complex loops.

I'm looking for either a rule of thumb or some explained examples like the above

Community
  • 1
  • 1
user56reinstatemonica8
  • 32,576
  • 21
  • 101
  • 125
  • I can't say definitively, but `var whatever` almost certainly doesn't count, as the statement isn't run at runtime. Other than that, it's _probably_ one line of code executing, be if `if( foo ){` or your long jQuery chain at the end. the jQuery chain, however, probably runs a few hundred lines of code from within the jQuery library. In other words, #1 is 0, #5 is 2 (separated by the semi-colon), all the others are 1. But you have to follow code paths to see how much code is actually run in #2, #5, #6, #7 –  Feb 12 '14 at 10:45

2 Answers2

2

What you are asking for can only be guessed unless some Microsoft IE8 developper comes and answer your question.
Being a puny Froggy with no connections with Microsoft whatsoever, I designed a simple test to try and measure how statements are counted:

<body>
<div id='div'></div>
<script>
var count = 5000000;
var res = 0;
var d = document.getElementById ('div');
function fun (aaa) // 1 statement
{
    aaa+= Math.random(); // 1 statement
    aaa+= Math.random(); // 1 statement
    aaa+= Math.random(); // 1 statement
}
function format (count) // 1 statement
{
    return Math.round(count*100/i)/100; // 1 statement
}
function format2 (count) // 1 statement
{
    var a = count*100; //4 statements
    var b = a / i;
    var c = Math.round (b);
    var d = c/100;
    return d; // 1 statement
}

for (var i = 0 ; i != count ; i++) // 2 statements
{
    res += Math.random();                      // 1 statement
    d.innerHTML = format2(count);              // 1 statement
    d.innerHTML = Math.round(count*100/i)/100; // 1 statement
    fun (res);                                 // 1 statement
}
</script>
</body>

I ran this script on IE8 over XP in a VirtualBox.

The number displayed in the page represents roughly the number of statements (as IE counts them) per loop. The imprecision is probably due to the asynchronous polling of the statements count, as explained in the Microsoft paper.

You can modify the code to see for yourself which kind of instructions are counted as statements.

My empirical observations so far :

It seems a statement is indeed close to the grammatical definition, i.e. anything found until the next ;.

  • a loop counts as 2 statements (the test and the indice update, probably)
  • a function call counts as 2 statements, regardless of the result being affected to a variable
  • an arbitrarily complex expression evaluation counts as a single statement
  • builtin functions dont count as statements, but each statement executed in user defined ones does.

It seems you could cram more power inside the 5M allowed statements by packing computations into more complex single statements.

All this being said, a design that uses a browser to perform heavy duty computations is fundamentally flawed, if you ask me.

kuroi neko
  • 8,479
  • 1
  • 19
  • 43
0

To my understanding browsers execute code off of a queue.

When some code is running and you create a setTimeout(), even if you set the timeout to 4 milliseconds (each browser may be different, not comment below), that code will not execute in parallel with other JavaScript code in the browser. Instead the function is put onto a queue.

When the currently executing script finishes it will then look on the queue for the next item to execute. The queue could have event handlers (from user input, though I think they may be prioritiezed, not sure) and things like Ajax call responses (like jQuery ajax call success code etc), they all get added to the queue and they are executed one at a time.

JavaScript always runs in a single thread, there is never two threads running JavaScript code in parallel. That one thread picks stuff off the queue and executes one at a time.

Note: You are better to not run things in big for loops as that will trigger the errors you are getting.

jwwishart
  • 2,865
  • 2
  • 23
  • 26
  • setTimeout of 0 has mixed results. It used to run immediately in some browsers, but I don't recall off the top of my head which ones. So a timeout of 1 is safer (and behaves exactly the same in most browsers) –  Feb 12 '14 at 10:47
  • @zyklus I thought that all browser made setTimeout(func, 0) into setTimeout(func, 4) or even greater by default now (i.e. there is a minimum lower bound that they will support) you may be right though, and I can see why it would be wise to do that (esp as we don't actually know how each and every browser deals with this.) Nice Point! – jwwishart Feb 12 '14 at 10:48
  • Most browsers do, but as I said some browsers used to read `setTimeout( func, 0 )` as `func()`. And they don't exactly "change it" to a `4ms`, `10ms`, whatever timer, it's just that that's the flush cycle of those browsers. If you have a browser with a 10ms flush and run a setTimeout with 1ms at the beginning of the cycle, it'll run 10ms later. If, however, you chew up 9ms of CPU time first, it'll run 1ms later. –  Feb 12 '14 at 10:51
  • "that code will not execute asyncronously" -- you have that backwards ;) –  Feb 12 '14 at 10:53
  • Ah, that does sound familiar. Thanks for the good feedback! @zyklus – jwwishart Feb 12 '14 at 10:54
  • I'm not sure this answers the question - regardless of whether it's *strictly* asynchronous, `setTimeout()` does restart the IE counter, even with `0` (MS say so themselves, and my testing shows it does help avoid the problem, but I've used it as much as is possible). My question is focussed on the definition IE uses to count executed statements. If I could avoid using loops, I would, but the script makes complex calculations over thousands of data points from a database and looping over the data is unavoidable (server side isn't an option unfortunately) – user56reinstatemonica8 Feb 12 '14 at 10:54