Paul Irish

Making the www great

Best Practice: Poll Instead of a setTimeout Hack

Very often you’ll have events happening asynchronously, but you need to wait until one has completed before you fire the second. And you may not have the ability of attaching a callback function to the first.

In my less wise days I’d say “Lets just setTimeout it for a couple seconds…” but always felt really dirty about it.

A classier approach I’ve used lately is to poll for a change. Here I’m using the gmail greasemonkey API and waiting for it to load in before I start using it:

1
2
3
4
5
6
7
8
9
10
11
12
13
gmonkey.load("1.0");

// this is a self-executing anonymous function that uses setTimeout to call itself 
// at 50ms intervals until the isLoaded variable resolves as true.

(function(){
  if (gmonkey.isLoaded){
    // do stuff i want to do with the API
    gmonkey.get('1.0').addNavModule('notepad', '<iframe src="http://aaronboodman.com/halfnote/"></iframe>');
  } else {
    setTimeout(arguments.callee,50);
  }
})();

I found myself doing this a lot when loading in multiple external resources and playing with them.. So to generalize the code I wrote executeWhenLoaded():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// executeWhenLoaded() will be overloaded with as many arguments as i want to check for presence.
function executeWhenLoaded(func){

  for (var i = 1; i<arguments.length; i++){ // for loop starts at 1 to skip the function argument.
    if (! window[ arguments[i] ]) {
      setTimeout(arguments.callee,50);
      return;
    }
  }

  func(); // only reaches here when for loop is satisfied.
}

// and in use:

executeWhenLoaded(function(){
    console.log(session.data);
},'session');   // session will return a value when the whatever preceding functionality is done. 

executeWhenLoaded’s first argument is the function to call, it can an unlimited number of arguments, which are all strings that reflect objects in the global namespace that have to be present in order to execute that function.

Update: In the comments, ProggerPete notes that this is not cross-browser compatible.. yet! In IE6, at least, the browser loses the original reference to the arguments object when it cycles through on the arguments.callee call. He offers a fix below.

Comments