Thinking Async

Share this:

Here's the rub: when you load JavaScript from a third party you should do it asynchronously. You might want to load your own scripts asynchronously too, but for this article let's focus on third parties.

There are two reasons for this:

If the third-party goes down or is slow, your page won't be held up trying to load that resource.

It can speed up page loads.

At Wufoo, we just switched over to an asynchronous embed snippet. Users who build forms with Wufoo and want to embed them on their site are now recommended to use it. We did it for exactly those reasons above. It's the responsible thing to do for a web service that asks people to link to resources on that services site.

Let's explore this whole async thing.

Uhm. What?

There is a little terminology involved here that will help us understand the umbrella "asynchronous" term.

"Parser blocking" - The browser reads your HTML and when it comes to a <script> it downloads that entire resource before moving on with the parsing. This definitely slows down page loads, especially if the script is in the head or above any other visual elements. This is true in older browsers as well as modern browsers if you don't use the async attribute (more on that later). From the MDN docs: "In older browsers that don't support the async attribute, parser-inserted scripts block the parser..."

To prevent problematic parser blocking, scripts can be "script inserted" (i.e. insert another script with JavaScript) which then forces them to execute asynchronously (except in Opera or pre 4.0 Firefox).

"Resource blocking" - While a script is being downloaded, it can prevent other resources from downloading at the same time as it. IE 6 and 7 do this, only allowing one script to be downloaded at a time and nothing else. IE 8 and Safari 4 allow multiple scripts to download in parallel, but block any other resources (reference).

Ideally we fight against both of these problems and speed up page loading (both actual and perceived) speed.

The HTML5 Way

There is an async attribute for the script tag in HTML5 (spec). Example:

<script async src="https://third-party.com/resource.js"></script>

The browser support for it is Firefox 3.6+, IE 10+, Chrome 2+, Safari 5+, iOS 5+, Android 3+. No Opera support yet.

If you are going to load a script directly like that, using this attribute is probably a good idea. It prevents parser blocking. These newer browsers don't have any big problems with resource blocking, but the parser thing is a big deal. We aren't using this at Wufoo because we need way deeper browser support than that.

The Classic Async Way

Here is the basic async script loading pattern that will get you as deep of browser support as you'd ever need:

They are setting the async attribute of the script to true before appending it. This is useful exclusively for Firefox 3.6 which is the only browser that doesn't do that by default. This can probably be omitted in most scenarios. Setting the script type definitely isn't needed.

As in the simple pattern above, the src is set using a protocol-relative URL. This is a darn useful way to load the script from either HTTP or HTTPS depending on the page that requested it. We absolutely need to do this at Wufoo, but unfortunately found that it throws an error in IE 6 with the default security settings. If you don't need IE 6 support, by all means use it.

They are appending the script to the head or the body, whichever is found first. Totally fine way to do it, but just as safe to look for a script element since the code itself is a script element.

I'm pretty sure this still qualifies as asynchronous because the resource they are loading is a script-injected script, meaning it won't parser block.

What if you need a callback?

Sometimes you need to load a third-party script, then once that script is loaded, fire off some custom code. That custom code probably calls some function defined in the third-party script with data that is unique to a specific page.

Typekit is in this situation. You need to load the Typekit JavaScript, then kick it off. Typekit is actually taking advantage of the fact that scripts block the parser. If your page is held up until their script is loaded, you won't see the "FOUT" (Flash Of Unstyled Text), usually only problematic in Firefox, but also problematic in Typekit where @font-face resources are being loaded via JavaScript.

This is clever, but it's slightly dangerous. If Typekit were to be down or slow: "What was once a desirable delay in rendering to hide the FOUT becomes a serious problem when the script takes longer than a few seconds to load." (reference).

There is an awful lot of gnarly code there to handle the onload callback. That's just the way it is to get callbacks working with deep browser support, unfortunately. Be forewarned, using this pattern actually brings back the problem of FOUT. If you want to go async with Typekit and have just as good an experience as you do normally, read their post on it which covers some clever class name manipulation and font events.

jQuery and other Script Loaders

If you are already using jQuery, loading up a third-party script and getting a callback when it's ready is pretty easy:

I'm sure other libraries have similar abilities. It's the classic thing JavaScript libraries are good at helping with. Also see getScript which might be a bit more succinct.

If you aren't using a library and are concerned about file size, YepNope is a super tiny script loader that can help as well. It's ideal use is performing a test to see if you need to load the script or not, but it has direct methods as well:

That set of key/value pairs is useful for a user to be able to see, change, and add things to. We toyed around with some ways we could keep that, but pass that data as part of the URL when calling the script. The script on our end would really be PHP and be able to $_GET the values. That would avoid needing to deal with all the ugly callback code in an async pattern. Possibly something like:

But ultimately, we decided against it. The callback code isn't that bad, stringifying JSON doesn't have very deep browser support (and it's impractical to include a polyfill in copy and paste code), and having our form.js well-cached ideal.

Social Media

Social media buttons are a classic case of needing third-party JavaScript on your page. Interestingly, the three biggest players provide their code in an async pattern already.

Cleaning House

All the above are very similar yet slightly different. Plopping them all down as-is on a page can make code purists like us cry. Nicholas Gallagher has a really clean efficient way to put them all together:

Dealing with CMSs

WordPress is friggin huge. So are all the other major CMS's. They can't be ignored when you're a third-party offering up copy-paste JavaScript code. The key, of course, is testing. The single most important thing is not including double line-breaks inside the code. Like:

Honestly, the "size" of the snippet was a concern. Fifty lines is just too much for something like this. It's at 19 now, which is more than we had, but is acceptable. Many of those lines are the options object, which we could squish up into fewer lines but it's nicer having them each on separate lines for read/changability.

We need to be supporting IE 6 still, so unfortunately no protocol-relative URL's for us. We're using the location.protocol test.

It's a bit bigger than your "average" async snippet (if there is such a thing as an average snippet), but that's OK. It's got quite a bit of work to do and it does it well.

We talk about a number of the advantages to it in the announcement blog post. My favorite is that you can now move the script to anywhere you'd like, it doesn't need to be exactly where you want the form to appear like it used to be.

Waterfalls

If you are interested in doing some testing on resource loading, looking at resource waterfalls is particularly useful. Modern web dev tools have this built in, like the "Network" tab in web inspector or "Net" tab of Firebug. But the old school dev tools in IE 6-8 don't offer that information. Fortunately the website Web Page Test does (it's kinda ugly but it's very cool).

While I was doing some testing for the Wufoo snippet in IE 6, I could prove our new way was non-blocking and the old was way blocking:

Alright that's all I got. I feel a little weird writing about all this stuff because it's all fairly new to me and I feel like I'm far from an expert. So feel free to correct me on anything or share your own async experiences.

Share this:

Comments

Where does the defer attribute fit in to all of this? It starts the (non-blocking, right?) download immediately but waits for the page to parse before execution, and if I remember correctly it also has the advantage of executing in order.

Also I could be wrong but I think one problem it avoids is the execution causing parsing (and/or maybe reflowing..?) to pause… Seems like a large/complex <script async> loading in the middle of parsing would cause some problems.

the src is set using a protocol-relative URL. This is a darn useful way to load the script from either HTTP or HTTPS depending on the page that requested it. We absolutely need to do this at Wufoo, but unfortunately found that it throws an error in IE 6 with the default security settings. If you don’t need IE 6 support, by all means use it.

I’m interested in what kind of errors in threw, and how you tested this.

At Wikipedia we’ve switched almost everything to protocol-urls since fall/winter of 2011 and do support IE6 as much as possible. I don’t remember getting any warnings (tested both on http and https)

The Deck uses a different pattern…I’m pretty sure this still qualifies as asynchronous because the resource they are loading is a script-injected script, meaning it won’t parser block.

Actually no, if you insert a script element using document.write in that way, the parser does block on the newly-added script element. This is a very common way to add further blocking scripts. You can try it here (source).

I think it’s important to mention that JavaScript containing document.write() (very common when working with 3rd party scripts) cannot be loaded asynchronously. At least not without hacks.

For instance, The Deck example does not render asynchronously because the script uses document.write() to inject the ad. Loading and parsing gets blocked. This is why sites like Daring Fireball are unable to render whenever The Deck network experiences an outage.

Here’s a demo page showing what happens when a script containing document.write() is inserted with document.write(). The response sleeps for 3 seconds to demonstrate the blocking.

Love the article. Typekit has always seemed like a dangerous script to load the way they offer the embed. I’ve been utilizing async (very similar to your snippet above) to load it, but definitely still get the FOUT. I like the link out to their article about class manipulation to help with that. Good stuff, thanks Chris!

Chris, one thing worth mentioning for the jQuery users out there is that using async JS will affect $(window).load(function(){}); which is common for firing off DOM manipulations once all the images are loaded on the page. The more 3rd party async calls are made, the greater the need for a timeout or error handeling on the window load event.

i think all the h5bp-kids out there don’t have to worry about blocking scripts, as they probably have modernizr included in their html-head and thereby access to the great Modernizr.load-method.

this actually makes all other script-loaders superflous. except for require.js. but that’s not a simple script-loader, but an approach to modularizing javascript and dependency management. one doesn’t need require.js just to load on or two external scripts asynchronally.

For the jQuery-based example, it’s probably a good idea to enable caching by specifying 'cache': true, like this: https://gist.github.com/1197140 If not, a timestamped query parameter will be appended to the request URL to ensure that the browser downloads the script each time it is requested — which is probably not what you want in this case.

It’s worth noting that jQuery.getScript() cachebusts by default, too. The only way to override that (other than using jQuery.ajax() and enabling the cache option) is to set the cache property globally:

Why not just put the script(s) at the bottom of the body tag? Unless your users are downloading tons of script file data (1MB+), the page will be responsive almost immediately anyway, and the scripts will have loaded before the users try to do anything (since the DOM and styles would have loaded already). Heck, it will actually load faster since the user doesn’t need to wait for an extra HTTP request for a file. That’s why we don’t slice images anymore :)

I guess it makes sense for multiple, larger, less important files, if they are observably hindering website performance. It just seems strange to me to make a user wait for an async load manager to load AND wait for a script to load asynchronously on top of that.

I think one of the most interesting asynchronous script loaders nobody seems to talk about is defer.js. Unlike any other solution it can easily load itself asynchronously. That means zero blocking scripts. All your “must execute” code can go inline just before CSS, as is recommended now.

I prefer to use a Plugin to all self-hosted WordPress users. The plugin name is: Async JS and CSS. Currently I am using this and Google page speed displaying empty render blocking Java script and CSS. Works great for me….

This comment thread is closed. If you have important information to share, please contact us.

Related

How do you stay up to date in this fast⁠-⁠moving industry?

A good start is to sign up for our weekly hand-written newsletter. We bring you the best articles and ideas from around the web, and what we think about them.

👋

CSS-Tricks* is created, written by, and maintained by Chris Coyier and a team of swell people. It is built on WordPress and powered up by Jetpack. It is made possible through sponsorships from products and services we like.