Simple JavaScript Requirement System for Asynchronous Scripts

I recently went through the chore of making sure that all the scripts on this site are loaded asynchronously using JavaScript Deferment, as shown in Django Template Tags for JavaScript Deferment. This ended up causing problems with my JavaScript, because most of my scripts depend on blog.js, which is now loaded asynchronously. I decided to write a simple require system that other scripts could use to ensure that blog.js is already loaded, and today I am sharing that simple system.

How do it…

A small bit of code needs to be added directly to the HTML, exposing the global function __onReady:

<script type="text/javascript">
(function(w) {
    var aFunctions = [];

    w.__onReady = function(fnCallback) {
        aFunctions.push(fnCallback);
    };

    w.__onReady.ready = function() {
        w.__onReady = function(fnCallback) {
            fnCallback(w);
        };

        for (var i = aFunctions.length - 1; i > 0; i -= 1) {
            w.__onReady(aFunctions[i]);
        }
    };
}(window));
</script>

The JavaScript code that is required (the dependency), should call the __onReady.ready function when it finishes (blog.js in my case):

__onReady.ready();

Finally, any code requiring the dependency should be wrapped inside of an __onReady call (in my case, all other JS files):

__onReady(function() {
    // code here as normal
});

How it works…

The global __onReady function must be added before any other JavaScript is included on your page, so you ensure it is always available. When the function is called (and the page is not yet ready) the passed callback function is added to an internal array of callback functions. If the page is ready the callback function is immediately executed and window is passed as the only argument.

When the __onReady.ready function is called, it overwrites the behavior of the __onReady function to return immediately, as the required JavaScript is already available. It then iterates through the callback functions in the internal array and calls each of them.

I have kept the __onReady code as small as possible, so it has little impact on the weight of the page. It also uses straight JavaScript, so you do not need to depend on a library (which should be loaded asynchronously anyway).

One powerful way you could use this, is to load jQuery (or another library) asynchronously but still depend on it in your scripts (also loaded loaded asynchronously). Just add __onReady.ready(); to the end of your library JavaScript file and wrap your other scripts using the __onReady callback pattern. Loading scripts (especially libraries) asynchronously will drastically decrease the delay before a page become usable to the end user.

There’s more…

This is not the only strategy I could have taken for managing dependencies on blog.js, but is the one I liked most. I could have split the file, so that the parts that have to run are included synchronously (probably directly in the HTML) and making blog.js a YUI module that is included by other services. Or I could have included another system, such as requireJS, but this strategy limited the complexity, while requiring few changes.