Working with a client one day, we started getting several reports of users having trouble using the site. They would click “add to cart” or almost any other button on the site, and nothing would happen. Usually when you hear something like this, the first suspect is JavaScript errors. After trying to reproduce with several browsers, I finally got the error:

$ is not defined

If you’re familiar with jQuery, you’ll know this means that something tried to use jQuery when it wasn’t loaded. I reloaded the page, and sure enough, no more error. Curious as to how that could happen, I took a look at the JavaScript that we were loading on every page.

There were two main JavaScript files: an “init.js” file which contained some JavaScript libraries including jQuery, and a “main.js” file which had the majority of the JavaScript to drive the functionality of the site. At the same time that I saw these files, I also spotted the problem: both of them had the “async” tag attribute:

The “async” attribute essentially tells the browser “don’t wait to download and execute this script before continuing to render the page”. The big benefit is that it generally improves page load time (and therefore that sweet, sweet SEO ranking). More than likely, the “async” attribute was added to these scripts at some point to speed up page load time.

However, keep in mind that adding that attribute means that the order the JavaScript files will execute in is arbitrary. The problem that was happening here is that the “main.js” was running before “init.js”, but “main.js” depended on “init.js” for jQuery and other libraries.

So how was the issue uncommon enough to get past QA? If you look at the snippet above, you’ll notice that “init.js” is at the top of the page, and “main.js” is at the bottom. This probably had a small factor in the browser loading the “init.js” first since it would parse that tag first. But the main reason is because of the differing file sizes. “init.js” was relatively small, containing jQuery and not much else. “main.js” contains JavaScript for almost the whole site and is several times larger than the other. The download speed was the main factor in getting “init.js” to run first. Once cached, the files would execute in order. While it’s cool how that worked out, it’s not exactly the most reliable way to handle this situation.

The easy “solution” would have been to just remove the “async” attribute from the “init.js” script, forcing it to always load before “main.js”. The obvious problem with that is we’ve tossed some of the performance benefit out the window unnecessarily.

Managing Interdependent Async JavaScript Files

So what’s a solution that will help us keep the performance benefits gained from “async” but also not introduce this broken dependency problem?

Solution #1: Chaining Scripts


This option involves JavaScript to the end of “init.js” that adds the dependent JavaScript file to the page once “init.js” has loaded. This method of creating the script is actually async by default. That way, the execution order of the JavaScript is guaranteed without blocking the page load. But this introduces another problem which we’ll come back to in a moment.

Solution #2: Execution list

Inline JavaScript:

If you don’t have the luxury of being able to modify the JavaScript file (like ones hosted by a 3rd party), you could go with a similar solution where you add JavaScript to the page that knows which JavaScript files to load and in which order. Once each asynchronous file has loaded, the next dependent JavaScript file can be added to the page.

The new problem: Waiting for downloads of JS files

Either of these methods has a downside: the browser won’t download the JavaScript files until they’ve been added to the page. It makes sense that this wouldn’t happen, because the browser doesn’t even know which files it needs until they’re on the page. Lucky for us, the browser gods are working on a solution.

The new solution: Preemptively downloading using preload

Introducing preload links

This preload link tells the browser “I’m going to need this file at some point, so start downloading it for me now, please :)”. Just throw one in the “head” for each asynchronous JavaScript file you’ll add to the page. That solves the exact problem we had- almost. Unfortunately, browser support is still a little lacking for this feature. Most major browsers except IE and Firefox support this (how on Earth did Edge beat Firefox to something?!). That said, it’s probably not the end of the world to lose out on the bit of time it takes to download the JavaScript files. And as the support does improve, so will your site’s performance!

So if you’re looking to improve your site’s page load performance, you can carefully start marking those JavaScript files as “async” and set up an execution order for interdependent files. Then, with the help of the “link” above, you can have them start downloading at the start of the page load just as they were before, all without blocking the page load.