Objects and SetTimeout
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.
February 26th, 2007 at 5:07 am
Several javascript forums and irc channels later, my problem is finally solved.
Special thanks to Kevin - a guy who really knows how to apply OOP theory to javascript.
The key is Object.prototype and within minutes I successfully managed to apply this technique to my own code:
http://www.christophercoggins.com/Temp_Refs/zximage_move_OOP.html
If anyone is interested, here is a stripped down version of Kevin’s code with the bare minimum to run (I changed item to my_item because my IDE says item is a keyword and changed my_item.countDown to setTimeout(process, 500) to show that you can do this in case you need to like myself):
function println(text)
{
document.body.innerHTML += text + “”;
}
function Person(name, cycles)
{
this.name = name;
this.cycles = cycles;
this.countDown();
}
Person.prototype.countDown = function()
{
var my_item = this;
var process = function()
{
println(my_item.name + “: ” + my_item.cycles);
my_item.cycles–;
if (my_item.cycles > 0)
{
//my_item.countDown();
setTimeout(process, 500);
}
}
setTimeout(process, 500);
}
new Person(”John”, 3);
new Person(”Sally”, 5);
BTW, in Kevin’s blog code above I believe self.count-; should be self.count–;
Once again, thanks a lot and this really opens up a lot of potential where storing your objects in arrays would bloat/complicate your javascript.
February 26th, 2007 at 7:43 am
FWIW, the post does use two dashes but WP blends them together into a single em dash. I’m having fits formatting code in WP for some reason.
February 26th, 2007 at 8:01 am
From copying and pasting the code it’s actually only 1 character.
I’ve seen this a lot on writing forums where it changes back and forth depending on whether the board supports this character (The — character is used a lot in writing).
Maybe —-(4 dashes instead of two) will create two big dashes?
I tried this out in wordpad and it worked, but it still looks like one.
February 26th, 2007 at 8:05 am
I got it now. I fooled it by using entity refs for each minus character: --.
February 27th, 2007 at 2:10 am
And man triumphs over machine yet again…
March 1st, 2007 at 8:52 am
Another claim to fame for Kevin: his SVG code has just been reused on the recently unveiled “price index calculator” on the very official French government Statistics Office at http://www.insee.fr/fr/indicateur/indic_cons/sip/sip.htm
March 1st, 2007 at 9:13 am
Thanks for the link, jmc!