My last blog post introduced a nifty little library called JSManager. I wanted to highlight a somewhat clever trick that is used to sequentially load registered JavaScript files.
Registered scripts are sequentially pushed into an array, and because I need to load them one after another to prevent conflicts, I can't simply iterate the array calling the load function:
// Would not work!
for (f in fileList)
{
loadJSInclude(fileList(f).FileName, fileList(f).callBack);
}
That technique would attempt to load all scripts immediately, which could cause load-order conflicts, and make it impossible to figure out when they are all loaded.
I needed a better technique, and the concept that I came up with is iterating an array using callbacks.
/* Loads all registered JS Files, then runs the passed callback */
Init : function (callback) {
// Setup callback chain
for (f in fileList) {
fileList[f].Callback = jsLoaderCallback(parseInt(f));
// Setup final callback
if (f == fileList.length - 1 && callback != null) {
fileList[f].Callback = jsLoaderCallback(parseInt(f), callback);
}
}
// Run the Chain
fileList[0].Callback();
}
This trick assigns a callback to each array element. Each callback performs the load operation, then calls the next callback in the array. The final callback is the "All Finished" callback provided by the user.
This allows me to iterate the array asynchronously, but only move to the next element when the previous one is completely done loading.
The actual callback function is this:
// Used to register the callback chain
function jsLoaderCallback(index, callback) {
return function () {
if ((index + 1) < fileList.length)
callback = fileList[index + 1].Callback;
if (fileList[index].Loaded == null) {
fileList[index].Loaded = true;
loadJSInclude(fileList[index].FileName, callback);
}
else {
callback();
}
};
}
The first thing you might notice is that this function simply returns a function. This is because of how javascript scopes closures and is usually called the "getRef()" trick. I'd recommend reading up on it to clarify this somewhat.
This technique could probably be generalized out into a handy set of pluggable functions, and I may do that in the future.
Posted by Jonathan Holland on 2/21/2009.