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:
<script>
var resource = document.createElement('script');
resource.src = "https://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 = 'https://third-party.com/resource.js';
s.parentNode.insertBefore(g, s);
}(document, 'script'));
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 = 'https://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 scripttype
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="https://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 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.
<script type="text/javascript" src="https://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 = 'https://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: 'https://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("https://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 = 'https://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.
<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 = "https://connect.facebook.net/en_US/all.js#xfbml=1&appId=200103733347528";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<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="https://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 = 'https://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:://ssl.google-analytics.com/ga.js', 'ga');
// Google+ button
add('https://apis.google.com/js/plusone.js');
// Facebook SDK
add('https://connect.facebook.net/en_US/all.js', 'facebook-jssdk');
// Twitter SDK
add('https://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://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 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.
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.browser support for
defer
is very poor. IE came up with it, but even IE8 doesn’t always run deferred scripts in the right order. Other modern browsers have limited, buggy, or no support.references:
mozilla.org
sitepoint.com
use
window.onload()
(or library functions, like jQuery’s$(document).ready()
method) instead.Chris, you mentioned YepNope but the script I’ve been more comfortable using is LABjs: http://labjs.com/ (there are quite a few of them out there now)
Whoa I never even knew about that one! thanks :)
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)
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
I’m also a huge fan of headjs, even if sometime it’s really hard to make it behave.
For async script loading i have recently turned to RequireJS which i have used on a couple of projects now and have found it to be quite consistent when viewing the network results.
Chris, I’m surprised you haven’t mentioned Require.js! How come you chose not to?
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
@Teddy Cross If you are interested in the
defer
attribute I would highly recommend you check out the discussion happening on Paul Irish’s lazy web request – Issue #42.https://github.com/paulirish/lazyweb-requests/issues/42
If you can’t be bothered clicking on the link. Paul summed it up with these wise words:
Hope that helps.
Oh and wicked post Chirs!
Hi Chris,
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).Best,
— T.J.
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.
If you’re using the current ad code, it’s already asynchronous.
http://www.webmaster-source.com/2011/03/18/google-rolling-out-asynchronous-adsense-ads/
But it’s only asynchronous *after* it has loaded the show_ads.js script in the first place, which is done synchronously.
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 withdocument.write()
. The response sleeps for 3 seconds to demonstrate the blocking.http://www.ravelrumba.com/code/demos/blocking-ads/demo-blocking2.html
Anyway, the point is just that there’s some cooperation required with whatever external script you’re loading to ensure asynchronicity.
hinclude.js is a great library for including async content.
http://mnot.github.com/hinclude/
document.write is evil… but it seems like it’s still loaded in parallel, albeit with some gotchas: http://stevesouders.com/cuzillion/?ex=10014
So cool article, thanks !! Especially with the Panda’s and Cafeine update, page load is more important than ever… Page bookmarked ;-)
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!
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!
“There is two reasons for this:”
rawr. grammar. meow.
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.
Hey Chris,
Thanks for sharing this.
jQuery also has the getScript function, a shorthand for your .ajax exmaple. http://api.jquery.com/jQuery.getScript/
Hate to nitpick. Your example Dealing with CMS above:
Remove the semicolon from foo:bar;
thx
You can also use callbacks like Google Maps API does:
kinda nag but classic way needs a close tag
thanks
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.
Hi Chris,
I think there’s room for improvement in your WuFoo new async snippet, specially in code size.
I’ve rewritten some parts of your snippet keeping the same functionality.
Have a look at this gist: https://gist.github.com/2011869
Hope you like it ;)
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.
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.
Thank you for sharing this, it’s much appreciated as usual.
Cheers,
Chris
Hey Chris,
the jQuery snippet is pretty neat!
But you might want to add
cache: true
, since jQuery avoids caching fordataType: script
through appending a query string by default.In most cases this would cause a client/server-side performance issue, so you might add a hint for those jQuery users, who don’t read the docs everyday ;)
We are happily using head.js for our last few projects. – It’s predictable and easy to understand.
For jQuery you should be using the
$.getScript()
function. It makes the code much more readable as far as what’s actually happening.Nice overview, Chris.
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 usingjQuery.ajax()
and enabling thecache
option) is to set thecache
property globally: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/
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!
Who in his right mind still thinks for IE6?!? Get over it.
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.
The jQuery example is slightly broken here is the small change:
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):
you can check the rest of it at github:
https://github.com/keriati/async-js
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.
Thanks for the article.
I came across your post after Google Page Speed Online recommended me to load the scripts async.
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.
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
Thanks Chris!
Did you tried Socialite.js https://github.com/tmort/Socialite —Nice also for social network
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….