My JavaScript book is out!
Don't miss the opportunity to upgrade your beginner or average dev skills.

Tuesday, May 24, 2011

setTimeout and setInterval with extra arguments ... once again!

Funny discussion today on twitter about "why on Earth IE still does not support extra arguments with setTimeout and setInterval" ... oh, well ...

The execScript Behaviour

Somebody in IE team thinks that the rest of the world should avoid extra arguments because of a bloody edge case as the third argument in IE is:

// ... seriously ...setTimeout("Msgbox('WTF')", 0, "VBScript");

What IE Users Could Do

Well, rather than create a closure every bloody time we would like to reuse a function with different arguments, something posible 10 years ago via ActionScript 1, every web developer (and not only) misses the opportunity to avoid closures using a de-facto standard for some unknown reason not part yet of ECMAScript specifications.For those interested I will show an example later, right now let's think about a solution compatible with VBScript for those mental developers, as I have been, brave enough to still use this language for some purpose.

setTimeout(function () { // a closure for *this* edge case only // rather than all cases "trapped" because of this! execScript("Msgbox('WTF')", "VBScript");}, 0);

Exactly The Same Behaviour!

Yes, if we use a string for setTimeout or setInterval this will be executed on the global scope, regardless where we defined this timer.Accordingly, the latter example via execScript does exactly the same, since execScript executes synchronously on the global scope, and once trapped behind a timer, nothing change, same result .... happy? No, you are not!

The Classic Closure

The most common situation where we have problems is when we have a portable function defined somewhere else and we would like to use this function passing certain arguments there.

Closures And Scope Lookup Costs

Every time we access an outer scope variables we do a lookup in the ... well, outer scope. Every time we create a closure we pass through a function expression activation plus we create a nested scope that has to perform a scope lookup to access the outer function/variable.Whenever this description makes sense or not, here the test you can try with not so powerful devices or mobile phones and tablet.In my Atom N270 netbook that test is quite explicit: 50% less performances for each nested closure and its inline invoke.

Speed UP!!!

I have already described this pattern but I keep seeing too few developers adopting it.

Speed UP MORE!!!

How many extra/redundant/superflous closures and inline invoke we have created? 0.

How Difficult It Is

.. not at all.It's pretty straight forward and it costs nothing for IE considering that you never bothered with this problem and you reached this point rather than skip this whole post at the beginning ... well, thanks for your attention :D , and this is your solution:

Not Obtrusive

If we use above script at the very beginning of our web page there are extremely rare chances that the next script won't be able to use already the fixed version of setInterval and setTimeout for IE only.If another script includes the same logic nothing will be redefined for the simple reason that variable one will be there so no double reassignment will be performed.In the very safe scenario, considering we are inside our bigger outer scope created for our library, we can define those references as internal:

We may eventually decide to use some "isIE" check via conditional comments on our pages, since the solution costs nothing once minified, and have a normalized de-facto, fast, easier, behavior for every other browser.Here the inline synchronous re-assignment for latter case:

Update ... and As Summary

Of course the lookup is much faster than function creation, and this is the dedicated test but this post is about the summary of lookup and the classic closure creation historically used only because of this IE inconsistency.Less lookup plus less closures around are faster, and numbers are there ( meaningful with slower devices )

4 comments:

Jarle
said...

Hi, Andrea.

Do You know if this will work on IE9? When I test it I'm not allowed to overwrite setTimeout/setInterval. I can overwrite window.setTimeout, but calling setTimeout is then not the same as calling window.setTimeout, that is window.setTimeout !== setTimeout. Before the assignment of the window.setTimeout the expression window.setTimeout === setTimeout is true.