Thursday, February 14, 2008

Javascript's setInterval / setTimeout

Kicking off asynchronous tasks is a common problem in programing languages, and while I have become very accostumed to managing threads and using timers in .Net, I found that two of the mechanism in Javascript for executing code asynchronously at specified intervals to have some odd pecularites.

If you google setInterval and setTimeout, you will learn that these are two methods that can be used delay execution of code by a certain number of milliseconds. setInterval will execute some javascript code every x milliseconds. I am going to focus on setTimeout (setInterval has the exact same semantics).

setTimeout takes two parameters, the first is either a string or a function pointer, and the second is an integer which is the number of milliseconds to wait before executing the code. For example setTimeout("alert('hello world')",10000); Will cause an alert box to be displayed after 10 seconds.

While this might be useful in some cases the more powerful form is to pass a function pointer to be executed. There are a couple key points to keep in mind when using the function pointers:

  • First is that you are to pass a reference to a function so the following will not work as expected:
    setTimeout(myObj.sayHello(var1),5000);

    This will result in the code being executiung immediatley. You will need to instead use: setTimeout(myObj.sayHello,5000);

  • Since you are limited to using a function pointer you cannot pass any arguments (There is a workaround for firefox but it is not supported in ie).


The question then becomes how do you pass in arguments. You might be tempted to use the string overload; however, when setTimeout evaluates a string the current context becomes window. This causes all references to this to evaluate to the window object.

To get around this you can create a javascript object which is nothing more then a function. Imagine the following code



function world(numPeople)
{

this.numberofPeople=numPeople;

this.sayHello=function(){alert("Hello to all " + this.numberofPeople + " people in the world");}

}

myObj=new world(1000);
setTimeout(myObj.sayHello,1000);


When we run this code we should get an alert box saying hello to 1000 people; however, we get an alert indicating that this.numberofPeople is undefined. The this keyword is pointing to the window object which doesn't have a definition for numberofPeople.

A small change to this object will make it work. And I am curious to the opinion of anyone who comes accross this blog as to if this is an ok solution.
function world(numPeople)
{

var numberofPeople=numPeople;

this.sayHello=function()
{
alert("Hello to all " + numberofPeople + " people in the world");
}

this.get_numberofPeople=function()
{
return numberofPeople;
}

}

myObj=new world(1000);
setTimeout(myObj.sayHello,1000);

alert(myObj.get_numberofPeople());


Here are changing the numberofPeople to a variable within the scope of the function world. This ensures that when the sayHello method is executed that the variable is still within scope; however, if you needed to also access the numberofPeople variable from outside of the world function you will need to add an accessor such as get_numberofPeople.

No comments: