JAL – Just Another Loader for JavaScript

A long time ago I saw the film "Interview with the vampire" starring Tom Cruise, Brad Pitt and Kirsten Dunst. The scene that struck me the most is when Pitt's character realizes that Lestat is using him in order to adapt to the current age. For a developer this is not a very bad rule. In fact it is actually quite good. If you want to keep up and stay on top, follow the bleeding edge, experiment and copy what others are doing. Reverse engineering and reinventing the wheel is a bliss. Apply this to open source and we – developers, hackers, designers – have a wide array of tools at our hands. Just think of "View Source" in the Web Browsers. Without it we wouldn't be where we are today. Copying is learning. Inventing is impossible without standing on the shoulders of our predecessors.

The company where I work, Tail-f Systems has just recently open sourced a small JavaScript library called JAL, which is an acronym for Just Another Loader. This is an infant project, it lacks certain features but does the job and does it well. It is, as the name implies, a tool for parallel conditional dependency loading of resource files. We use it in our Web UI for loading scripts and CSS files. It is there for one reason only: To speed things up!

We tested YepNope, which is a great loader, but felt it could be faster. It also had features we didn't really need. So we wrote our own. We reinvented the wheel. How hard could it be? Well, it was pretty hard.

What we needed was a resource loader that could load, not only JavaScript but stylesheets as well. It also needed to be able to load resources in parallel and in groups to handle dependencies, such as loading jQuery before loading a jQuery plugin. The final requirement was conditional loading, i.e. load JSON.js if the browser is lacking native JSON support.

Parallel dependency loading

A typical setup looks something like this:

$loader
    .load('js/shape.js')
    .load([
          'js/circle.js'
        , 'js/rectangle.js'
    ])
    .load('js/square.js')
    .ready(function() {
        // Start app
    })

There are three dependency groups set up. The first one loads a shape. The second loads a circle and a rectangle, which are dependent on shape. The last group contains a square which is derived from a rectangle. In this trivial example, the speedup happens in the second group since the circle and the rectangle are loaded in parallel. Now, imagine you have a large number of scripts with different dependencies in your application. The traditional way is to concatenate all the scripts into one large bundle and then minify that bundle. What you are actually doing is that you are loading your scripts the old fashioned way, one after another. Modern browser are capable of loading scripts and resources in parallel. They actually open up multiple connections to a web server and load multiple resources all at the same time. So if you have a script that takes, say, 5 seconds to load and you break that into 5 pieces and load the pieces in parallel the loading time becomes, theoretically, 1 second. That is five times faster than before!

Conditional loading

Now to conditional loading. Conditional loading is where you load a resource if a certain condition is met. Does the browser have native JSON support? No? Well, we'll fix that! Here's an example of loading a JSON polyfill:

$loader
    .when(typeof window.JSON === 'undefined', function(loader) {
        loader.load('js/json.js')
    })

Done is done

Once a resource group has loaded, JAL allows you execute code. Here is an example where the "ready" event in jQuery is halted until all scripts have loaded.

$loader
    .load('js/jquery.min.js')
    .done(function(){
        // Stop jQuery from triggering the "ready" event
        $.holdReady(true)
    })
    .load([
          'js/script-one.min.js'
        , 'js/script-two.min.js'
    ])
    .ready(function() {
        // Allow jQuery to trigger the "ready" event
        $.holdReady(false)
        // Start app
    })

How it was done

Writing JAL was both challenging and fun. The most difficult part was to make sure that the load order was respected between the groups. This was tricky since things were happening fast and there was a big performance difference between the browsers.

JAL was implemented using a resource queue and a polling function. The queue is locked until a resource group has been loaded. Once loaded the "done" event is fired. This allows you to inject one or more resource groups to the front of the queue, if you ever need that. After the "done" event has been triggered the queue is unlocked and the poller is free to load the next resource group.

The poller itself is started once the loader sequence has been executed. This is done by pushing the poller to the top of the script stack using setTimeout with a timeout of 0 milliseconds. It's a classic example of how the single threaded model of a web browser’s JavaScript engine can be used.

Closing words

Do you have a large concatenated JavaScript file? Is it minified and gzipped? Is it loading fast? Do you want faster? Then minify and gzip your resource files individually and use a conditional parallel dependency loader instead.

About Helgi Kristjansson

Helgi Kristjansson is a JavaScript viking hailing from Iceland. He invaded Sweden in 1998 where he has worked mainly developing web based user interfaces in hardware related industries. His current employer is Tail-f Systems - a Stockholm based company. You can reach Helgi on Twitter at @djupudga

More articles by Helgi Kristjansson…


7 comments

  1. Colin Jack

    “Do you have a large concatenated JavaScript file? Is it minified and gzipped? Is it loading fast? Do you want faster? Then minify and gzip your resource files individually and use a conditional parallel dependency loader instead”

    Was wondering what tests you performed on this?

    For example what sort of a speed increase were you getting in practice and did you have any issues using this approach with web sites used by mobile devices?

    November 22nd, 2012 at 04:54

  2. Johan

    This only works if enough parallel requests to a server are allowed and if the user has enough bandwidth, otherwise the overhead of making multiple HTTP requests will make it actually _slower_ than putting everything in one file.

    November 22nd, 2012 at 05:24

  3. Helgi Kristjansson

    Colin: My assumption in this statement is that one is including a script on a web page in the traditional way. The speed increase is dependent on many factors. One of those is that by using a resource loader you are not blocking the loading of the rest of the page. Another is available bandwidth, web server setup and, of course, what sort of a web browser a visitor is using. Then there is the question of perceived speedup by the user. We haven’t done any heavy testing of the speed increases in our app – our product does not have that stringent requirements – the driving factor was our requirement for dependency loading and for that we built upon what people like Steven Souder have been writing about. Also, we are not a web shop. Tail-f is a product development company.

    It works on iPad and iPhone 4 without any issues.

    Johan: You are absolutely right. The use case and the target audience matter. In our product we have about 5 mb of unminified JavaScript and we can also dictate what browsers we support. However, if you have a fair amount of scripts and have power over the web servers, dependency loading could be a viable alternative.

    November 22nd, 2012 at 07:17

  4. Steve Souders

    My first question in evaluating a script loader is “Does the script loader itself block the page?” Unfortunately, JAL is loaded synchronously – it blocks the page from rendering. It’s ironic that a script loader intended to promote asynchronous behavior is itself synchronous. The alternative of inlining JAL’s 7K of JS in every HTML document also hurts performance. ControlJS is an example of a script loader that is itself loaded asynchronously. I’d love to see more loaders follow this pattern – the last thing we need is for every script in the page to be blocked by a synchronous script.

    November 22nd, 2012 at 11:49

  5. Helgi Kristjansson

    Asynchronous loading of JAL is good idea, thanks for that input. However, for the product I am working on I believe delayed execution, such as in ControlJS, will the yield the greatest improvement in speed. Makes for a faster app start. Thanks for the good comment.

    November 22nd, 2012 at 13:13

  6. JorisW

    I have been doing this on the server side. All CSS and JavaScript code is concatenated into two big files (precompile). Only scripts that are requires to fulfill a dependency are included in the resulting file.
    The resulting javascript file is included using the attribute async.

    I have seen a lot of speed improvements using this method.

    November 23rd, 2012 at 07:52

  7. Brett Zamir

    While this looks well done, I am curious why didn’t you go with http://requirejs.org if you knew about it? That seems like a well thought out project using a virtual standard means of loading modules.

    Admittedly, it doesn’t have your nice syntax for conditional loading–especially necessary in a world requiring shims with browsers ever-evolving. (I requested this at https://github.com/jrburke/requirejs/issues/520#issuecomment-10834588 )

    November 28th, 2012 at 21:27

Comments are closed for this article.