Archive for February, 2007

Objects and SetTimeout

Sunday, February 25th, 2007

I got an interesting question from Chris Coggins regarding the use of objects and setTimeout. Oftentimes, we want state associated with each invocation of the timeout handler. The challenge comes from the fact that when our setTimeout code fires, it runs as if it were defined in the global scope. This means we only have access to what’s available in that scope: global properties.

Ideally, setTimeout could fire a function as if it were a method. That would make “this” inside the function’s body refer to the particular instance firing the function. That in turn would give us something to which we could attach state for each setTimeout invocation. Unfortunately, that’s not an option with today’s implementation of setTimeout, so we need a workaround.

In the past, I would enqueue the object containing my state, call setTimeout, and then dequeue the object in the timer handler. This gets a bit ugly since you have to maintain arrays (still somewhat globally) and it’s fairly easy to imagine a situation where two setTimeouts can interfere with one another if they share the same array. We get around these issues via a JavaScript closure.

The first important point is that setTimeout implementations allow the first parameter to be a function reference as opposed to a string. The second important point is that we can make a closure by creating a function literal. The closure allows our function literal to access variables that were in its scope when it was defined. To make this a bit clearer, let’s create a Person object that counts down at a regular interval.

function Person(name, count) {
    this.name = name;
    this.count = count;
}

Person.prototype.countDown = function() {
    var self = this;
    var process = function() {
        println(self.name + “: ” + self.count);

        self.count–;

        if (self.count > 0) {
            self.countDown();
        }
    }

    setTimeout(process, 500);
}

When the “process” function is called via setTimeout() “this” would refer to global instead of our Person instance. We create a “self” local variable as a sibling to the function literal. This makes “self” visible to the function literal’s body regardless of where it actually executes. This is one of the beauties of closures. We have effectively glued the active Person instance to the “process” function, so when it fires, it can access that instance.

To make sure we really have multiple contexts, we can test with something like the following:

function init() {
    var john = new Person(”John”, 3);

    john.countDown();

    // delay the other counter
    setTimeout(init2, 250);
}

function init2() {
    var sarah = new Person(”Sarah”, 5);

    sarah.countDown();
}

You can see a sample at:

/tutorials/javascript/inheritance/count-down.htm

Enjoy.