Wednesday, May 14th, 2008

Polling for loaded content instead of simple setTimeout

Category: JavaScript, Library, Tip

Have you ever found yourself doing little setTimeout calls as you wait for content to be loaded asynchronously? It seems to happen pretty frequently, and Paul Irish has created a simple utility to help run code when the library you need is loaded.

With his executeWhenLoaded(function, objects, that, must, be, present) you can do something like this:

javascript

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

The implementation is simply:

function executeWhenLoaded(func){

for (var i = 1; i

Posted by Dion Almaer at 8:58 am
7 Comments

++---
2.8 rating from 20 votes

7 Comments »

Comments feed TrackBack URI

Nice one! Will be using this for sure.

http://www.subprint.com

Comment by JoeMcCann — May 14, 2008

Simpler than trying to detect loading of script/code dynamically I guess (er, via attaching onload/oncomplete handlers to script nodes and so on.) This of course assumes that you are or will be actually loading said script. ;) 50 msec might be a bit aggressive (imagine ~20 calls a second ideally), but at the same time you do want a snappy response when the function does become available.

Comment by Schill — May 14, 2008

Another option would be to use anonymous events and avoid the polling. Objects can subscribe to an event string like “subscribe(‘session loaded’)”. When the session.js script loads, it’s last line is “fire(‘session loaded’)”. This way an object can subscribe to events before there even exists a subject that will fire the event.

http://peter.michaux.ca/article/32

Comment by PeterMichaux — May 14, 2008

Is that utility released under a GPL license or BSD?

Comment by Jordan1 — May 14, 2008

This has some significant problems which prevent it from working.

It seems that when the function recurses, it looses all arguments, including the arguments passed in.

function executeWhenLoaded(func){
// for loop starts at 1 to skip the function argument.
for (var i = 1; i
function executeWhenLoaded(func){

var args = [].slice.call(arguments, 1), j = 0;
poll();
function poll() {
for (var i = 0; i
This is a bare minimum required change to make the code actually work.

http://dhtmlkitchen.com/ape/test/adhoc/test-execWhenLoaded.html

Of course, it will still only work with truthy values of args[i]. It would be clearer to not use varargs, but to change the signature to:

executeWhenLoaded(func, argsArray);

Garrett

Comment by dhtmlkitchen — May 14, 2008

Your blog does not escape HTML entities (preview feature?). Retyping…

The code has some significant problems which prevent it from working.

It seems that when the function recurses, it looses all arguments, including the arguments passed in.


function executeWhenLoaded(func){
// for loop starts at 1 to skip the function argument.
for (var i = 1; i < arguments.length; i++){

// If the object window['someprop'] is not present,
if (! window[ arguments[i] ]) {

// recurse, omitting all arguments. (bug)
setTimeout(arguments.callee,50);
return;
}
func(); // only reaches here when for loop is satisfied.
}

The code will only appear to work, and will not cause error when there are no recursive calls. What will happen once executeWhenLoaded recurses (called as arguments.callee), is that there will be 0 arguments passed in, the loop will have no iterations, and then func(), which is not defined (was not passed back in), will cause a ReferenceError.

This can be demonstrated by creating a document which is not loaded right away, then calling the – executeWhenLoaded – function, passing in a reference to something in the bottom of the page (that thing must have truthy value).

http://dhtmlkitchen.com/ape/test/adhoc/test-execWhenLoaded-orig.html

IOW, this code is broken.

It would be possible to make it work through use of a closure, storing the original arguments as an array in the outer function.


function executeWhenLoaded(func){

var args = [].slice.call(arguments, 1), j = 0;
poll();
function poll() {
for (var i = 0; i < args.length; i++){ // for loop starts at 1 to skip the function argument.
if (! window[ args[i] ]) {
setTimeout(poll, 50);
return;
}
}
func(); // only reaches here when for loop is satisfied.
}
}

This is a bare minimum required change to make the code actually work.

http://dhtmlkitchen.com/ape/test/adhoc/test-execWhenLoaded.html

Of course, it will still only work with truthy values of args[i]. It would be clearer to not use varargs, but to change the signature to:

executeWhenLoaded(func, argsArray);

Garrett

Comment by dhtmlkitchen — May 14, 2008

@Peter,
Yeah a subscribe/fire custom event setup would be optimal, but I’ve certainly been in the position where I can’t modify half of the code. That’s why this solution came to be.

@dhtmlkitchen
You’re totally right. Thanks for the code you left. A similar comment was left on my blog with the same patch.

@Jordan1
It’s MIT licensed. Go wild.

Comment by PaulIrish — May 22, 2008

Leave a comment

You must be logged in to post a comment.