Script Loading

Effective JavaScript file loading techniques.

Script loaders can make a significant difference in page load times.



Overview


Script loading is important if your goal is to build fast loading web apps. Loading a large number of script files by placing multiple <script> tags in the header will slow down your app because they are loaded and executed before any HTML markup is rendered. Minifying your scripts helps but it does not change the fact that script loading does block page rendering.

Script loaders are tools that can alleviate this problem by allowing the load process to take place asynchronously and/or in parallel (i.e. multiple script files at the same time). It is important to do performance and comparison testing, but in many cases script loaders make a difference in page load times.

The truth is that script loaders are not always necessary. If your goal is to minimize page render times, then an easy way to make a difference is by concatenating all script files into a single file and placing the script tag towards the end of the file, just before the closing </body> tag. Once loaded, browsers will do a good job caching this file.

However, merging all files is not always possible; for example when your script tags reference CDN hosted files, such as on Google, Microsoft and Yahoo, rather than pulling in files from your server.

Script loaders mostly follow a similar pattern to indicate what needs loading. Here is some generic code to show what it looks like:

ScriptLoader.load("jQuery.js", "app.js");

This will load the two named scripts asynchronously which will speed up loading. Unfortunately this may pose a problem for other scripts that have dependencies on the above referenced scripts because when they execute it is not known whether these two files have completed loading or not.

Most script loaders offer a ready event to this timing issue. This event will fire when all scripts have completed loading and are available for use. You pass it a callback that will execute at that time. The syntax looks something like this:

ScriptLoader.ready(function () {
    // everything is loaded
}

Most script loaders allow you to combine the two above snippets in a single call in which you pass an array with scripts that require loading and a callback function which will be invoked when all scripts have completed loading, like so:

ScriptLoader.load(["jQuery.js", "app.js"], function () {
     // callback called when loading has completed
});

In case you're wondering how this works under the hood, most of these loaders simply insert a <script> element in the DOM document. Here is some skeleton code to get the idea:

var tag = document.createElement("script");
var src = http://mysite.com/js/app.js;
var first = document.getElementsByTagName("script")[0];

first.parentNode.insertBefore(tag, first);

This will insert the new script tag before the first <script> in the document, which most likely is a reference to the script loader's own JavaScript file.

There are a number of script loaders available. The most popular ones include Require.js, Head.js, curl.js and LazyLoad (by the way, the name LazyLoad is a reference to the Lazy Load pattern, i.e. load on demand).

Some script loaders have a narrow focus and all they do is speed up file loading. Others offer additional services which we will look at in a moment. LazyLoad falls under the first category: it allows you to speed up the load processing by loading CSS and JavaScript files on demand. LazyLoad is a tiny tool which when minified is less than 1K. Here is an example of how you use it with two JavaScript files:

LazyLoad.js(["jQuery.js", "app.js"],function () {
    // executes when loading is complete
});

The js method loads the files in parallel. If your files need to be loaded sequentially and in order, then the statements can be nested:

LazyLoad.js("jQuery.js",function () {
    // executes when jQuery is complete
    LazyLoad.js("app.js", function () {
       // executes when both are complete
    });
});

LazyLoad has the ability to load CSS files in a similar way by using its css method.

Other script loaders do more than just loading scripts; they also help with organizing large applications. The reason for this dual purpose is that script loading is inextricably related to the organization of your modules and the management of their dependencies. Modularity is the topic of the next section followed by a review of these dual purpose loaders.