start blog post

Passing Arguments to setTimeout and setInterval

(skip intermediate solutions)

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)) { ... }

var tags = [];

  • share this post:
  • email a friend
  • float this post
  • digg this post
  • share on stumbleupon
  • submit to technorati
  • tweet this post

end blog post