Often, we need to use setTimeout and setInterval
but also need to pass arguments to the function being called. There are a handful of ways to do this.
// Example 1, BAD! :(
var hello = 'Hello World!';
setTimeout("alert('" + hello + "')", 1000);
Example 1 works, but it is not the generally accepted syntax for setTimeout. Passing code as a string relies on the
same behavior as eval, and eval is usually evil for reasons that are beyond the scope of this post... No
pun intended. As an aside, this method is often restricted by Adobe AIR, because
of the implied eval. Here's the common workaround:
// Example 2, better :)
var hello = 'Hello World!';
setTimeout(function() {
alert(hello);
}, 1000);
Using a closure is a much better solution than supplying a string as in the first example, but it can seem bulky. Turning our little alert into a closure has basically doubled the amount of code required! Okay, it's still short, but it's not quite as clear as what we would like to do:
// Example 3, best! :D
var hello = "Hello World";
setTimeout(alert, 1000, hello);
The call to setTimeout in Example 3 is clear about what arguments are being passed, rather than having to
read through the closure to see what variables it might refer to. The good news is that Example 3 already works
on modern browsers (including Adobe AIR). The common implementation for both setTimeout and setInterval
allows you to pass additional arguments by simply... passing additional arguments. Awesome.
The exception to this rule is, of course, IE. At least up through version 7, but probably including version 8, IE
will simply ignore extra arguments supplied to setTimeout and setInterval. It won't even throw an
error, which can make this situation hard to debug (and is probably why this method is not as widely seen as using a
closure). However, here's a bit of code I originally ran across on Web Reflection
that extends this behavior to IE.
// Example 4, keep reading if this is confusing, don't worry...
/*@cc_on
(function(f) {
window.setTimeout = f(window.setTimeout); // overwrites the global function!
window.setInterval = f(window.setInterval); // overwrites the global function!
})(function(f) {
return function(c, t) {
var a = [].slice.call(arguments, 2); // gathers the extra args
return f(function() {
c.apply(this, a); // passes them to your function
}, t);
};
});
@*/
First things first, NO you do not have to retype the above code every time you want to use setTimeout.
Example 4 actually changes the way setTimeout and setInterval behave. You would include Example 4
at the top of your script, first (or early) in the code. Then you can call setTimeout and setInterval
with additional arguments the same way across all browsers. This code makes Example 3 work
everywhere.
Of special note is the conditional comment, /*@cc_on ... @*/. If you use a JavaScript minifier, it may
consider that whole block of code a comment and delete it... Not good. A less semantic but more reliable alternative is to use object
detection to identify IE, typically something like if (("ActiveXObject" in window) && !("opera" in window)) { ... }
I'm a Senior Culinary Software Developer at Yummly working on various front-end and middle-tier tasks, primarily using JavaScript. I blog about web design and development topics including accessibility, usability, performance, and developing HTML / CSS / JavaScript applications on Appcelerator Titanium and Adobe AIR.
If you're a web developer, you might enjoy Jelo, my JavaScript library.
All original work on this site is covered by a Creative Commons Attribution 3.0 license unless otherwise specified.
You may share or use any code or images from this site in any manner, for free, so long as reasonable effort has been made to give credit where due.
The views expressed in the posts and comments on this blog do not necessarily reflect those of my current or former employers.