Grow your CSS skills. Land your dream job.

Thinking Async

Published by Chris Coyier

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:

  1. If the third-party goes down or is slow, your page won't be held up trying to load that resource.
  2. 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="http://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:

<script>
  var resource = document.createElement('script'); 
  resource.src = "//third-party.com/resource.js";
  var script = document.getElementsByTagName('script')[0];
  script.parentNode.insertBefore(resource, script);
</script>

Here's a more efficient version with a wrapper around those variables (credit Mathias Bynens):

(function(d, t) {
    var g = d.createElement(t),
        s = d.getElementsByTagName(t)[0];
    g.src = '//third-party.com/resource.js';
    s.parentNode.insertBefore(g, s);
}(document, 'script'));
Update: Note that this is no longer recommended! The era that made this a useful pattern is over, and you should mostly just use the async attribute.

Ad Networks

BuySellAds is an advertising network that was one of the first to deliver ads asynchronously. This is their pattern:

<script type="text/javascript">
(function(){
  var bsa = document.createElement('script');
     bsa.type = 'text/javascript';
     bsa.async = true;
     bsa.src = '//s3.buysellads.com/ac/bsa.js';
  (document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(bsa);
})();
</script>

Couple of things to note here:

  • 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.

The Deck uses a different pattern:

<script type="text/javascript">
//<![CDATA[
(function(id) {
 document.write('<script type="text/javascript" src="' +
   'http://connect.decknetwork.net/deck' + id + '_js.php?' +
   (new Date().getTime()) + '"></' + 'script>');
})("DF");
//]]>
</script>

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.

<script src="http://third-party.com/resource.js"></script>
<script>
  doSomethingFancy('chriscoyier');
</script>

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 you're 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.

<script type="text/javascript" src="http://use.typekit.com/abc1def.js"></script>
<script type="text/javascript">try{Typekit.load();}catch(e){}</script>

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).

Here's a way to load Typekit async style:

<script type="text/javascript">
  TypekitConfig = {
    kitId: 'abc1def'
  };
  (function() {
    var tk = document.createElement('script');
    tk.src = '//use.typekit.com/' + TypekitConfig.kitId + '.js';
    tk.type = 'text/javascript';
    tk.async = 'true';
    tk.onload = tk.onreadystatechange = function() {
      var rs = this.readyState;
      if (rs && rs != 'complete' && rs != 'loaded') return;
      try { Typekit.load(TypekitConfig); } catch (e) {}
    };
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(tk, s);
  })();
</script>

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:

$.ajax({
  url: '//third-party.com/resource.js',
  dataType: 'script',
  cache: true, // otherwise will get fresh copy every page load
  success: function() {
    // script loaded, do stuff!
  }
}

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:

yepnope.injectJs("//third-party.com/resource.js", function () {
  // script loaded, do stuff!
});

Relevant: Max Wheeler's article Loading Typekit Asynchronously with YepNope.

Do you really need a callback?

At Wufoo, we figured yes, we need a callback. We need to load the form-embedding JavaScript resource, then call a function with all the specifics of the user's form. This is how we used to do it:

<script type="text/javascript">var host = (("https:" == document.location.protocol) ? "https://secure." : "http://");document.write(unescape("%3Cscript src='" + host + "wufoo.com/scripts/embed/form.js' type='text/javascript'%3E%3C/script%3E"));</script><br />
<script type="text/javascript">
var z7p9p1 = new WufooForm();
z7p9p1.initialize({
'userName':'chriscoyier', 
'formHash':'z7p9p1', 
'autoResize':true,
'height':'546', 
'ssl':true});
z7p9p1.display();
</script>

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:

script.src = '//wufoo.com/form.js?data=' + JSON.stringify(options);

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.

Facebook

<div id="fb-root"></div>
<script>(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=200103733347528";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>

Twitter

<a href="https://twitter.com/share" class="twitter-share-button">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

Google Plus

<g:plusone annotation="inline"></g:plusone>
<script type="text/javascript">
  (function() {
    var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
    po.src = 'http://apis.google.com/js/plusone.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
  })();
</script>

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:

(function(doc, script) {
    var js, 
        fjs = doc.getElementsByTagName(script)[0],
        add = function(url, id) {
            if (doc.getElementById(id)) {return;}
            js = doc.createElement(script);
            js.src = url;
            id && (js.id = id);
            fjs.parentNode.insertBefore(js, fjs);
        };

    // Google Analytics
    add(('https:' == location.protocol ? '//ssl' : '//www') + '.google-analytics.com/ga.js', 'ga');
    // Google+ button
    add('http://apis.google.com/js/plusone.js');
    // Facebook SDK
    add('//connect.facebook.net/en_US/all.js', 'facebook-jssdk');
    // Twitter SDK
    add('//platform.twitter.com/widgets.js', 'twitter-wjs');
}(document, 'script'));

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:

<script type="text/javascript">
var s = d.createElement(t), options = {

   foo: bar

}
// The line break space above is bad!
</script>

That might look nice and clean, but the "autop" behavior of WordPress will insert paragraph tags around various parts of that, which of course prevent the script from executing as expected.

Final Wufoo Snippet

This is what we ended up with:

<div id="wufoo-z7w3m7">
Fill out my <a href="http://examples.wufoo.com/forms/z7w3m7">online form</a>.
</div>
<script type="text/javascript">var z7w3m7;(function(d, t) {
var s = d.createElement(t), options = {
'userName':'examples', 
'formHash':'z7w3m7', 
'autoResize':true,
'height':'260',
'async':true,
'header':'show', 
'ssl':true};
s.src = ('https:' == d.location.protocol ? 'https://' : 'http://') + 'wufoo.com/scripts/embed/form.js';
s.onload = s.onreadystatechange = function() {
var rs = this.readyState; if (rs) if (rs != 'complete') if (rs != 'loaded') return;
try { z7w3m7 = new WufooForm();z7w3m7.initialize(options);z7w3m7.display(); } catch (e) {}};
var scr = d.getElementsByTagName(t)[0], par = scr.parentNode; par.insertBefore(s, scr);
})(document, 'script');</script>

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 you're "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.

Comments

  1. 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.

  2. Krinkle
    Permalink to comment#

    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)

  3. Niek
    Permalink to comment#

    What do you think of this script to load js in parallel: http://headjs.com/
    I think this is also a great way of loading js

  4. Chris, I’m surprised you haven’t mentioned Require.js! How come you chose not to?

  5. strages
    Permalink to comment#

    can anybody help me a little with the async social button loading from nicolas gallagher? i need to put in the async=1 into the script, but dunno when i would break it.

    https://gist.github.com/1025811

  6. Scott
    Permalink to comment#

    Do you know if it’s possible to load Google Adsense asynchronously? I tried a few things last week with window.onload but couldn’t get it to work at all.

  7. hinclude.js is a great library for including async content.

    http://mnot.github.com/hinclude/

  8. document.write is evil… but it seems like it’s still loaded in parallel, albeit with some gotchas: http://stevesouders.com/cuzillion/?ex=10014

  9. So cool article, thanks !! Especially with the Panda’s and Cafeine update, page load is more important than ever… Page bookmarked ;-)

  10. Permalink to comment#

    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!

  11. Thanks so much for this rundown! I have quite a few things I’ve made that get injected into client sites and I’m starting to update them all to async thanks to this article!

  12. “There is two reasons for this:”

    rawr. grammar. meow.

  13. Nate Walton
    Permalink to comment#

    One thing to note is that Modernizr actually includes the YepNope script loader, which is awesome since I’m already loading Modernizr for the HTML5 fallbacks and feature detects.

  14. Antonio Lettieri
    Permalink to comment#

    Hate to nitpick. Your example Dealing with CMS above:

    
    
    var s = d.createElement(t), options = {
    
       foo: bar; 
    
    }
    // The line break space above is bad!
    
    
    

    Remove the semicolon from foo:bar;

  15. You can also use callbacks like Google Maps API does:

    <script>function initialize() {
      var myOptions = {
        zoom: 8,
        center: new google.maps.LatLng(-34.397, 150.644),
        mapTypeId: google.maps.MapTypeId.ROADMAP
      }
      var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
    }
    
    function loadScript() {
      var script = document.createElement("script");
      script.type = "text/javascript";
      script.src = "http://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&sensor=TRUE_OR_FALSE&callback=initialize";
      document.body.appendChild(script);
    }
    
    window.onload = loadScript;</script>
  16. Kevin
    Permalink to comment#

    kinda nag but classic way needs a close tag

  17. 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.

  18. Do you know if it’s possible to load Google Adsense asynchronously? I tried a few things last week with window.onload but couldn’t get it to work at all.

  19. 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.

  20. Permalink to comment#

    Thank you for sharing this, it’s much appreciated as usual.

    Cheers,
    Chris

  21. Permalink to comment#

    We are happily using head.js for our last few projects. – It’s predictable and easy to understand.

  22. Stefan
    Permalink to comment#

    There is a script that allows you to get past the document.write issue. It’s called Ghostwriter: http://digital-fulcrum.com/solutions/ghostwriter-complete-control/

  23. Loading external resources in the async way is a MUST!

    We load asynchronously even CSS in some of our projects…

    Obviously we load async stylesheets that are needed only after an “action” by our users or in the case the CSS contains some optional (codemirror css for example) rules for HTML elements.

    We use the “standard way” to load async js/css “appending” a script/link element to the head.

    Btw, thanks for this article!

  24. Permalink to comment#

    Who in his right mind still thinks for IE6?!? Get over it.

  25. 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.

  26. Richard
    Permalink to comment#

    The jQuery example is slightly broken here is the small change:

    $.ajax({
      url: '//third-party.com/resource.js',
      dataType: 'script',
      success: function() {
        // script loaded, do stuff!
      }
    })
  27. I did something like this a few weeks ago. I like doing the callback part “facebook” style, also checking if the script was already loaded (but this part can be removed ofc):

    
        window.myAsyncInit = function() {
            AsyncApp.init({
                option1: 'string',
                option2: 3.14,
                option3: true
            });
        };
    
        (function(d,s){
            var i = 'async-script-id', j,
                r = d.getElementsByTagName(s)[0];
            if (d.getElementById(i)) {return;}
            j = d.createElement('script');
            j.id = i;
            j.async = true;
            j.src = "async.js";
            r.parentNode.insertBefore(j, r);
        }(document, 'script'));
    
    

    you can check the rest of it at github:
    https://github.com/keriati/async-js

  28. Great round up Chris!

    For social button like “Tweet” and “Like” you might enjoy Socialite.js http://socialitejs.com — it’s a specialist script loader I’m working on for social networks that can defer loading indefinitely.

  29. Thanks for the article.
    I came across your post after Google Page Speed Online recommended me to load the scripts async.

  30. You could also use the <script id="blah"> + getElementById('blah').addEventListener('load') way to do something after the async script is executed.

    Libraries like RequireJS and so on handle parallel loading and final callback events to avoid complex nested code.

  31. Permalink to comment#

    This is good !
    But we can’t detect JS loaded or not in Google Map API to execute our callback function.
    Google always load more than a javascript file in thei API code…

    Let’s review https://maps.googleapis.com/maps/api/js?sensor=false

  32. Thanks Chris!

    Did you tried Socialite.js https://github.com/tmort/Socialite —Nice also for social network

  33. Vesa Piittinen

    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.

  34. 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, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".