Over the past couple of years, a lot of attention has been paid to blocking and non-blocking JavaScript in browsers, and with good reason. JavaScript can both block both the rendering of a web page as well as causing the page to become unresponsive. Thanks largely to Steve Souders and his research, engineers now know how important it is to use non-blocking JavaScript in pages. There are three ways to create non-blocking scripts in JavaScript.

The first is to use the defer attribute on <script> tags. By adding defer, the browser starts to download the JavaScript file immediately but does so in a way that doesn’t block rendering or downloading of other resources on the page. For example:

<script type="text/javascript" defer src="foo.js"></script>

When the defer attribute is present, the script(s) will not execute until after the entire page’s code has been loaded. Deferred scripts execute before the DOMContentLoaded event and should execute in the order in which they appear in the document (this isn’t necessarily the case in Internet Explorer). The defer attribute is supported in Internet Explorer 4, Firefox 3.5, Safari 5, and Chrome 7.

The second is to use the HTML5 async attribute on <script> tags. Async scripts also begin to download immediately and in a non-blocking manner:

<script type="text/javascript" async src="foo.js"></script>

The difference between async and defer is that async scripts execute as soon as the script is downloaded, so the page may still be loading when the script actually executes. Another difference is that the order of execution for async scripts is explicitly not guaranteed, so an async script that appears later in the document might actually execute before one that appeared earlier in the page. The only guarantee is that async scripts will execute before the load event.

The third, and most popular, technique is to use dynamic script tags that are created via JavaScript. For example:

var script = document.createElement("script");
script.type = "text/javascript";
script.src = "foo.js";
document.getElementsByTagName("head")[0].appendChild(script);

When inserting a script dynamically, the non-blocking download begins immediately. The script executes as soon as it is downloaded completely. In most browsers, the order of execution is not guaranteed, though Firefox < 4 and Opera will execute the scripts in the order in which they were inserted. This general approach is supported in all major browsers.

These three techniques for non-blocking JavaScript ensure that the downloading of the resource doesn’t block either rendering or the download of other resources on the page during page load. The little-known or understood aspect of all three approaches is that they are all defined to block the load event. That means adding scripts using any of these techniques during page load will delay execution of the window.onload event handler until all scripts have executed. All browsers obey this behavior except for Internet Explorer (even through version 9).

Depending on your needs, delaying the load event may be okay, but generally you want this event to fire as quickly as possible across all browsers. If you’re using a progressively enhanced design where it’s okay for JavaScript to be loaded later, you may want to consider delaying the addition of script tags using a timer:

//doesn't block the load event
setTimeout(function(){
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "foo.js";
    document.getElementsByTagName("head")[0].appendChild(script);
}, 0);

By using a timer with a delay of 0, the code executes as soon as possible after the initial page load is complete. Keep in mind that this doesn’t guarantee the code will execute either before or after the window.onload event handler, it only guarantees that the script download does not block the load event. If you want to ensure that the JavaScript doesn’t start to download or execute until after the load event, you can insert it using the window.onload event handler:

//doesn't block the load event
window.onload = function(){
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "foo.js";
    document.getElementsByTagName("head")[0].appendChild(script);
};

This is the last piece of information you need to make the best decision as to when a page’s JavaScript should be loaded and with which technique. Deferred scripts, async scripts, and dynamically loaded scripts all will prevent the load event from firing until they’ve downloaded and executed; by adding a timer, you can ensure the script doesn’t block the load event and your window.onload event handler fires as quickly as possible.

ABOUT THE AUTHOR
Nicholas Zakas photo

Nicholas C. Zakas is a principal engineer at Yahoo!, where he is front-end tech lead for the Yahoo! homepage and a contributor to the YUI library. He is the author of Professional JavaScript for Web Developers (Wrox, 2009), Professional Ajax (Wrox, 2007), and High Performance JavaScript (O'Reilly, 2010). Nicholas is a strong advocate for development best practices including progressive enhancement, accessibility, performance, scalability, and maintainability. He blogs regularly at http://www.nczonline.net/ and can be found on Twitter via @slicknet.

37 Responses to “The truth about non-blocking JavaScript”

  1. Tweets that mention Performance Calendar » The truth about non-blocking JavaScript -- Topsy.com

    [...] This post was mentioned on Twitter by Stoyan Stefanov and Nicholas C. Zakas, Blaze. Blaze said: RT @stoyanstefanov: Performance calendar day 13 (boo!) @slicknet on non-blocking JS http://perfplanet.com/201013 #webperf #wpo [...]

  2. Philip Tellis

    Most modern browsers already download scripts in parallel so they don’t really block. It’s becoming less important for web devs to hack around it.

  3. Dave Hulbert

    With the window.onload example, the script won’t start downloading until the onload event fires. What’s best is having it download (without blocking) immediately, then inserted into the DOM as a script onload.

    You should be able to do this with a document.createElement(“object”), which the browser will cache.

  4. JulienW

    And what are the bad consequences of having onload firing late ?

  5. DS

    And what about putting scripts at the end of the body tag ?

  6. Dave Hulbert

    @Phillip IE7 doesn’t, but it depends on your stats as to whether that’s important or not.

    @JulienW, if you have to deal with a lot of legacy or 3rd party code then a late onload could be a problem.

    @DS, some web site/applications can output the header very quickly, but because of large amounts of dynamic content, the closing body tag could take more than a second to get to the user.

  7. Mathias Bynens

    Nice write-up!

    In case anyone is interested, I recently wrote an article with some more information on the setTimeout(fn, 0) trick to speed up window.onload.

    P.S. Why bother setting script.type = "text/javascript"; in all your examples? The code will work fine if you omit it. (In HTML5, it’s optional anyway.)

  8. Steve Souders

    In general, scripts loaded dynamically via setTimeout still block the load event in Firefox, Chrome, Safari and Opera (but not IE). See this example: http://stevesouders.com/tests/settimeout.php

  9. cancel bubble

    @Dave Hulbert

    “You should be able to do this with a document.createElement(“object”), which the browser will cache.”

    But won’t that cache the script without parsing and executing it? Ideal if you’re just pre-loading the script for a reasonable subsequent page the user is expected to land on.

  10. Iouri Goussev

    Also there are tools that allow you to load you js files asynchronously but if you specify their dependency in correct order.

  11. Kyle Simpson

    I was just about to post that I’m seeing the same behavior as Steve’s comment says. It appears setTimeout still blocks onload in everything but IE. LABjs uses setTimeout()s, but window.onload still waits.

    But, I’m curious, why is there still a practical need for window.onload? I mean, I understand if you want to hook in some code to fire after all content is finished, but why wouldn’t DOM-ready be just as fine for that (since all content would have already STARTED downloading)?

    Basically, I’m just not understanding why it’s a big deal if onload gets blocked?

  12. La veille du week-end (dixième) | LoïcG

    [...] Performance calendar day 13 : non-blocking JS : via stoyanstefanov [...]

  13. Satya Prakash

    Cool! Very compact information regarding non-blocking javascript. Thanks for the article.

  14. Blogenman

    Very interesting article. On the same subject but with more details about cross-brower compatibility, I recommend an article from Steve Souders : http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/

  15. JavaScript Magazine Blog for JSMag » Blog Archive » News roundup: ControlJS, Tutti, Minute With Node.js, Tipped

    [...] Installing Node and npm (by Isaac Schlueter on the Joyent blog) Global eval() in JavaScript The truth about non-blocking JavaScript (Nicholas Zakas) Strict Mode Is Coming To Town (Douglas Crockford) Announcing ECMAScript 5.1 Share and [...]

  16. Jorge

    They can also be document.write()n before onload, see:
    http://jorgechamorro.com/cljs/071/

    Jorge.

  17. Best-of-the-Web 3 | davblog: webdev and stuff

    [...] The truth about non-blocking JavaScript – Alle bekannten Arten des Scriptloadings [...]

  18. josephj

    IE behaving differently really causes a problem for me.
    I use YUI contentready to execute functions as soon as html block loads.
    However, YUI contentready depends on window.onload event.

    If I use non-blocking JavaScript, it won’t trigger window.onload in most of situations. The value of _loadComplete variable would always be false and has infinite loop. And my code fails. Really need to find a workaround for this issue.

  19. ゆっくりと… » “遅いブログパーツを高速表示する方法”を検証する

    [...] 少し例は違いますが、The truth about non-blocking JavaScript の中で Steve Souders が 指摘 しているように、setTimeout を使っても onload イベントがブロックされることがあるようです。 [...]

  20. Performance Calendar » The art and craft of the async snippet

    [...] The art and craft of the async snippetby Stoyan Stefanov JavaScript downloads block the loading of other page components. That’s why it’s important (make that critical) to load script files in a non-blocking asynchronous fashion. If this is new to you, you can start here or here. [...]

  21. My Ultimate Technique to Inject Javascript | dansajin.com

    [...] the setTimeout wrapper the non-blocking curlScript would block the load event as explained in The truth about non-blocking JavaScript. If we don’t care that much when the load event fires – or need it to fire as by [...]

  22. quibids software

    Well, I agree with what you wrote, but not with all of it. Regardless, it is all beneficial material. Thanks!

  23. Jeevanandam

    Till days I have used the third approach for Social buttons loading for my blog. Loading sign keeps on…

    Now I have modified the code using window.onload with setTimeout. Loading sign disappears, load event fires and with timeout call social buttons loads perfect.

    Thanks for info.

    ~Jeeva

  24. My Favorite Resources for JavaScript Optimization - Tom Conte

    […] The truth about non-blocking JavaScript […]

  25. toby

    I’ve found that calling document.write() inside a timer to load a script, even before the page has completed, both takes precedence over a document.write() issued in an inline script and also flattens the page.

    Full code and explanation:
    https://github.com/tobyattagman/docwriteandtimers

    I’d be interested in hearing if anyone else has seen such things and perhaps whether I’ve done something daft (other than using document.write, that is).

  26. Performance Blog: How to Get the Most Out of JavaScript Part II - DBS Interactive | DBS Interactive

    […] HTML documents that include JavaScript  so as to reduce the well documented effects of “render blocking” by scripts. By improving the rendering speed, we can improve the overall performance and, […]

  27. The art and craft of the async snippet | SharePointDevWiki.com

    […] JavaScript downloads block the loading of other page components. That’s why it’s important (make that critical) to load script files in a non-blocking asynchronous fashion. If this is new to you, you can start here or here. […]

  28. best seo company

    I am no longer positive where you are getting your info,
    but good topic. I needs to spend a while studying much more or working
    out more. Thank you for wonderful information I used to be in search of this info for my mission.

    my homepage :: best seo company

  29. atlanta mortgage rate

    One of the most frequently asked questions by individuals interested in a career in the mortgage industry is if they need a license.

    This will drop your score because the bureau sees this as
    someone who uses too much credit. If you want to get more loans to the table faster and earn more money, I strongly advise you at least have a look at my mortgage system and give it a try.

  30. av receiver test

    av receiver test
    Greetings from California! I’m bored to death at work so I decided to browse your blog on my iphone during lunch break.
    I really like the knowledge you present here and can’t wait
    to take a look when I get home. I’m amazed at how quick your blog loaded on my phone ..
    I’m not even using WIFI, just 3G .. Anyways, very good site!
    av receiver test

  31. http://www.vakuumpresse24.de

    kreditvergleich
    you’re in reality a good webmaster. The site loading pace is amazing.
    It seems that you are doing any distinctive trick.

    Moreover, The contents are masterwork. you have done a
    excellent job on this subject! kreditvergleich

    Here is my weblog: http://www.vakuumpresse24.de

  32. Lindskog.Blogspot.com

    It started with i – Phone and possesses now gone
    to live in i – Pad 2; the reason- not enough availability.
    With anywhere near this much controversy
    clinging to its name, it should come as no real
    surprise that several in the world’s police officers authorities have been
    looking for ways to shut it down for good.
    The most enjoyable facet of Revenge in the Fallen is its sheer familiarity.

  33. the

    Hi I am so grateful I found your weblog, I really found you by
    mistake, while I was browsing on Digg for something else,
    Nonetheless I am here now and would just like to say cheers for
    a remarkable post and a all round thrilling blog
    (I also love the
    theme/design), I don’t have time to read it all at the moment but I have book-marked it and also added your RSS feeds, so when I have time
    I will be back to read more, Please do keep up the excellent job.

  34. test gefrierschraenke

    gefrierschrank test
    Hi, I check your blogs like every week. Your writing style is witty, keep it up!
    gefrierschrank test

    Feel free to surf to my web page; test gefrierschraenke

  35. pokemon games for mobile

    Hi there, I discovered your blog by means of Google whilst
    searching for a comparable topic, your site came up, it looks good.

    I’ve bookmarked it in my google bookmarks.
    Hello there, just turned into aware of your weblog through Google, and located that
    it’s really informative. I am gonna watch out for brussels.

    I’ll be grateful in case you proceed this in future. A lot of
    people will be benefited out of your writing. Cheers!

  36. smartphone test

    smartphone test
    Nice weblog right here! Additionally your website lots up fast!
    What web host are you the use of? Can I am getting your affiliate
    link for your host? I want my website loaded up as fast as yours lol smartphone test

  37. dampfreiniger

    dampfreiniger test
    Write more, thats all I have to say. Literally, it seems as though
    you relied on the video to make your point. You definitely know
    what youre talking about, why waste your intelligence on just posting videos to your site when you could be giving
    us something informative to read? dampfreiniger test

Leave a Reply

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
And here's a tool to convert HTML entities