However, processing scripts asynchronously isn’t a yes-no decision. When we implemented automated async for the Blaze Web Performance Optimization service, we learned that there are many variations of async to consider. The right approach can vary for different sites, pages, scripts and even devices. In this post, I’ll go over the different levels of Async JS, demonstrate their impact on different pages and discuss how to decide what approach works best for your situation.
Level 1: No Async JS
The first level is the simplest one – let the browser do its thing. Scripts run inline within the page, block rendering, and block downloading of other resources, namely images.
It’s hard to call this level “Sync JS”, since browsers do perform various look ahead actions, and 3rd party components on the page may still set the async flag themselves. However, it’s clearly not “Async JS”, since most scripts on the page block resources in a very non-async manner.
If we look at the NY Times website, we can see this blocking in action. The next image is a piece of a visual filmstrip of the page loading. You can see the page is being built step-by-step, each second adding some more content. This is the effect of scripts – in this case 3rd party ads – blocking the rendering (and download) of everything that follows.
Level 2: Async JS
The next level is taking scripts off their pedestal, and running them in parallel to the rest of the resources on the page. This means scripts are loaded in a way similar to images, without blocking other downloads or rendering. We simply call this “async JS”, since scripts are running asynchronously to the rest of the page load.
Since we’re taking away the “high priority” usually given to scripts, scripts have to contend with other resources on the page for bandwidth and CPU. Therefore, when using Async JS scripts may complete a bit later than they would have in Level 1. That said, we’ve observed that on most pages, scripts still complete at around the same time, while the rest of the page renders and completes loading faster.
It’s important to know that performing Async JS also requires some form of script preloading (a.k.a. in-page prefetching). Without preloading, any implementation of Async JS will have to download scripts sequentially, just before they’re getting executed. Doing so will not harm the non-scripted content, but it will greatly delay the processing of the scripts themselves. At Blaze we prefer preloading into HTML5 localStorage where possible, but tools like LABjs, controljs and others use other techniques. Preloading usually happens at the very top of the page, to get the data ASAP and start executing it.
Level 3: Async Download, Defer Execution
Now that we’ve separated the loading of scripts from the web page, we can go a step further and separate the preloading of scripts from the execution of them. The next level of Async JS is to preload the scripts as early as possible (i.e. at the top of the page), but to defer the actual execution of the page till after page load.
Another advantage of deferring execution is consistent load times. As observed by Aaron Peters and others, running scripts asynchronously doesn’t always prevent Google Analytics and other RUM tools from counting them in the load times. The same is true for the browsers’ progress indicators (progress bar in chrome, spinning circle in IE, etc). Those may continue until the async scripts are complete.
Level 4: Defer Download & Execution
The last level of async JS is complete script deferral. This means pushing both the script download and the script execution till after the page load.
This last step means static content on the page doesn’t need to contend with scripts for bandwidth, and thus runs that much faster. The scripts, on the other hand, get delayed further, since they don’t even get downloaded before the page load completes. Therefore, this last step biases the page fully in favour of static content.
Using this technique is most useful in low-bandwidth networks. In these networks download bandwidth is precious, so not contending for it makes a real impact. In addition, full deferral especially helps sites where scripts make up a large portion of the total page size. If scripts make up 5% of the total page size, they wouldn’t take up much of the download bandwidth anyway. If they make up 30% of the total size, it’s a different story.
Deferring scripts is therefore especially useful for Mobile. Mobile networks are slow, and so using the download bandwidth wisely can really help. In addition, Mobile websites are often much lighter, and scripts make up a bigger portion of them. Therefore, we recommend (and default to) using this technique for mobile websites.
Scripts may download and run at various levels of priority within a page.
If your page is quite usable without any scripts, pat yourself on the shoulder, and proceed to implementing script deferral (level 3 or 4). This will help you capitalize on the better page architecture, and will make your page load time that much faster.