You are previewing Web Performance Daybook Volume 2.

Web Performance Daybook Volume 2

Cover of Web Performance Daybook Volume 2 by Stoyan Stefanov Published by O'Reilly Media, Inc.
  1. Web Performance Daybook, Volume 2
  2. SPECIAL OFFER: Upgrade this ebook with O’Reilly
  3. Foreword
  4. From the Editor
  5. About the Authors
    1. Patrick Meenan
    2. Nicholas Zakas
    3. Guy Podjarny
    4. Stoyan Stefanov
    5. Tim Kadlec
    6. Brian Pane
    7. Josh Fraser
    8. Steve Souders
    9. Betty Tso
    10. Israel Nir
    11. Marcel Duran
    12. Éric Daspet
    13. Alois Reitbauer
    14. Matthew Prince
    15. Buddy Brewer
    16. Alexander Podelko
    17. Estelle Weyl
    18. Aaron Peters
    19. Tony Gentilcore
    20. Matthew Steele
    21. Bryan McQuade
    22. Tobie Langel
    23. Billy Hoffman
    24. Joshua Bixby
    25. Sergey Chernyshev
    26. JP Castro
    27. Pavel Paulau
    28. David Calhoun
    29. Nicole Sullivan
    30. James Pearce
    31. Tom Hughes-Croucher
    32. Dave Artz
  6. Preface
    1. Conventions Used in This Book
    2. Using Code Examples
    3. Safari® Books Online
    4. How to Contact Us
  7. 1. WebPagetest Internals
    1. Function Interception
    2. Code Injection
    3. Resulting Browser Architecture
    4. Get the Code
    5. Browser Advancements
  8. 2. localStorage Read Performance
    1. The Benchmark
    2. What’s Going On?
    3. Optimization Strategy
    4. Follow Up
  9. 3. Why Inlining Everything Is NOT the Answer
    1. No Browser Caching
    2. No Edge Caching
    3. No Loading On-Demand
    4. Invalidates Browser Look-Ahead
    5. Flawed Solution: Inline Everything only on First Visit
    6. Summary and Recommendations
  10. 4. The Art and Craft of the Async Snippet
    1. The Facebook Plug-ins JS SDK
    2. Design Goals
    3. The Snippet
    4. Appending Alternatives
    5. Whew!
    6. What’s Missing?
    7. First Parties
    8. Parting Words: On the Shoulders of Giants
  11. 5. Carrier Networks: Down the Rabbit Hole
    1. Variability
    2. Latency
    3. Transcoding
    4. Gold in Them There Hills
    5. 4G Won’t Save Us
    6. Where Do We Go from Here?
    7. Light at the End of the Tunnel
  12. 6. The Need for Parallelism in HTTP
    1. Introduction: Falling Down the Stairs
    2. Current Best Practices: Working around HTTP
    3. Experiment: Mining the HTTP Archive
    4. Results: Serialization Abounds
    5. Recommendations: Time to Fix the Protocols
  13. 7. Automating Website Performance
  14. 8. Frontend SPOF in Beijing
    1. Business Insider
    2. CNET
    3. O’Reilly Radar
    4. The Cause of Frontend SPOF
    5. Avoiding Frontend SPOF
    6. Call to Action
  15. 9. All about YSlow
  16. 10. Secrets of High Performance Native Mobile Applications
    1. Keep an Eye on Your Waterfalls
    2. Compress Those Resources
    3. Don’t Download the Same Content Twice
    4. Can Too Much Adriana Lima Slow You Down?
    5. Epilogue
  17. 11. Pure CSS3 Images? Hmm, Maybe Later
    1. The Challenge
    2. Getting My Hands Dirty with CSS3 Cooking
    3. Cross-Browser Results
    4. Benchmarking
      1. Payload
      2. Rendering
    5. Are We There Yet?
    6. Appendix: Code Listings
      1. HTML
      2. CSS
  18. 12. Useless Downloads of Background Images in Android
    1. The Android Problem
    2. And the Lack of Solution
  19. 13. Timing the Web
    1. Conclusion
  20. 14. I See HTTP
    1. icy
    2. Some details
    3. Walkthrough
    4. Todos
    5. The Road Ahead
    6. All I Want for Christmas…
  21. 15. Using Intelligent Caching to Avoid the Bot Performance Tax
  22. 16. A Practical Guide to the Navigation Timing API
    1. Why You Should Care
    2. Collecting Navigation Timing Timestamps and Turning Them into Useful Measurements
    3. Using Google Analytics as a Performance Data Warehouse
    4. Reporting on Performance in Google Analytics
    5. Limitations
    6. Final Thoughts
  23. 17. How Response Times Impact Business
  24. 18. Mobile UI Performance Considerations
    1. Battery Life
    2. Latency
    3. Embedding CSS and JS: A Best Practice?
    4. Memory
      1. Optimize Images
      2. Weigh the Benefits of CSS
      3. GPU Benefits and Pitfalls
      4. Viewport: Out of Sight Does Not Mean Out of Mind
      5. Minimize the DOM
    5. UI Responsiveness
    6. Summary
  25. 19. Stop Wasting Your Time Using the Google Analytics Site Speed Report
    1. Problem: A Bug in Firefox Implementation of the Navigation Timing API
    2. Solution: Filter Out the Firefox Timings in Google Analytics
    3. Good News: The Bug Was Fixed in Firefox 9
    4. Closing Remark
  26. 20. Beyond Web Developer Tools: Strace
    1. What About Other Platforms?
    2. Getting Started
    3. Zeroing In
    4. Example: Local Storage
    5. We’ve Only Scratched the Surface
  27. 21. Introducing mod_spdy: A SPDY Module for the Apache HTTP Server
    1. Getting Started with mod_spdy
    2. SPDY and Apache
    3. Help to Improve mod_spdy
  28. 22. Lazy Evaluation of CommonJS Modules
    1. Close Encounters of the Text/JavaScript Type
    2. Lazy Loading
    3. Lazy Evaluation to the Rescue
    4. Building Lazy Evaluation into CommonJS Modules
  29. 23. Advice on Trusting Advice
  30. 24. Why You’re Probably Reading Your Performance Measurement Results Wrong (At Least You’re in Good Company)
    1. The Methodology
    2. The Results
    3. Conclusions
    4. Why Does This Matter?
    5. Takeaways
  31. 25. Lossy Image Compression
    1. Lossy Compression
  32. 26. Performance Testing with Selenium and JavaScript
    1. Recording Data
    2. Collecting and Analyzing the Data
    3. Sample Results
    4. Benefits
    5. Closing Words
    6. Credits
  33. 27. A Simple Way to Measure Website Performance
    1. Concept
    2. Advantages
    3. Limitation
    4. Conclusion
  34. 28. Beyond Bandwidth: UI Performance
    1. Introduction
    2. After the Page Loads: The UI Layer
    3. UI Profilers
      1. CSS Stress Test
      2. CSS Profilers
      3. CSS Lint
      4. DOM Monster
    4. Perception of Speed
    5. Tidbits
    6. Call for a Focus on UI Performance
  35. 29. CSS Selector Performance Has Changed! (For the Better)
    1. Style Sharing
    2. Rule Hashes
    3. Ancestor Filters
    4. Fast Path
    5. What Is It Still Slow?
  36. 30. Losing Your Head with PhantomJS and confess.js
    1. Performance Summaries
    2. App Cache Manifest
    3. Onward and Upward
  37. 31. Measure Twice, Cut Once
    1. Identifying Pages/Sections
    2. Identifying Features
    3. Optimizing
  38. 32. When Good Backends Go Bad
    1. What Is a Good Backend Time?
    2. Figuring Out What Is Going On
    3. Fixing It
    4. Finally
  39. 33. Web Font Performance: Weighing @font-face Options and Alternatives
    1. Font Hosting Services Versus Rolling Your Own
    2. What the FOUT?
    3. Removing Excess Font Glyphs
    4. JavaScript Font Loaders
      1. Introducing Boot.getFont: A Fast and Tiny Web Font Loader
    5. Gentlefonts, Start Your Engines!
      1. My Observations
    6. Final Thoughts
  40. About the Author
  41. Colophon
  42. SPECIAL OFFER: Upgrade this ebook with O’Reilly
  43. Copyright

Chapter 4. The Art and Craft of the Async Snippet

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 nonblocking asynchronous fashion. If this is new to you, you can start with this post on the Yahoo User Interface (YUI) library blog ( or the Performance Calendar article (

In this post, I’ll examine the topic from the perspective of a third party—when you’re the third party, providing a snippet for other developers to include on their pages. Be it an ad, a plug-in, widget, visits counter, analytics, or anything else.

Let’s see in much detail how this issue is addressed in Facebook’s JavaScript SDK.

The Facebook Plug-ins JS SDK

The Facebook JavaScript SDK is a multipurpose piece of code that lets you integrate Facebook services, make API calls, and load social plug-ins such as the Like button (

The task of the SDK when it comes to Like button and other social plug-ins is to parse the page’s HTML code looking for elements (such as <fb:like> or <div class="fb-like">) to replace with a plug-in. The plug-in itself is an iframe that points to something like with the appropriate URL parameters and appropriately sized.

This is an example of one such plug-in URL:

The JavaScript SDK has a URL like so:

The question is how do you include this code on your page. Traditionally it has been the simplest possible (but blocking) way:

<script src=""></script>

Since day one of the social plug-ins though, it has always been possible to load this script asynchronously and it was guaranteed to work. Additionally, a few months ago the async snippet became the default when SDK snippet code is being generated by the various wizard-type configurators.

Figure 4-1 shows how an example configurator looks like.

Like button configurator

Figure 4-1. Like button configurator

The async code looks more complicated (it’s longer) than the traditional one, but it’s well worth it for the overall loading speed of the host page.

Before we inspect this snippet, let’s see what some of the goals were when designing a third-party provider snippet.

Design Goals

  • The snippet should be small. Not necessarily measured in number of bytes, but overall it shouldn’t look intimidating.

  • Even though it’s small, it should be readable. So no minifying allowed.

  • It should work in “hostile” environments. You have no control over the host page. It may be a valid XTHML-strict page, it may be missing doctype, it may even be missing (or have more than one) <body>, <head>, <html> or any other tag.

  • The snippet should be copy-paste-friendly. In addition to being small that means it should just work, because people using this code may not even be developers. Or, if they are developers, they may not necessarily have the time to read documentation. That also means that some people will paste that snippet of code many times on the same page, even though the JS needs to be loaded only once per page.

  • It should be unobtrusive to the host page, meaning it should leave no globals and other leftovers, other than, of course, the included JavaScript.

The Snippet

The snippet in the Facebook plug-in configurators looks like so:

<script>(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); = id;
  js.src = "//";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>

Take a look at what’s going on here.

On the first and last line you see that the whole snippet is wrapped in an immediate (a.k.a., self-invoking, aka self-executing) function. This is to assure that any temporary variables remain in the local scope and don’t bleed into the host page’s global namespace.

On line 1, you can also see that the immediate function accepts three arguments, and these are supplied on the last line when the function is invoked. These arguments are shorthands to the document object and two strings, all of which are used more than once later in the function. Passing them as arguments is somewhat shorter than defining them in the body of the function. It also saves a line (vertical space), because the other option is something like:

<script>(function() {
  var js, fjs = d.getElementsByTagName(s)[0],
      d = document, s = 'script', id = 'facebook-jssdk';
  // the rest...

This would be one line longer (remember we want readable snippet, not overly long lines). Also the first and the last line will have “unused” space as they are somewhat short.

Having things like the repeating document assigned to a shorter d makes the whole snippet shorter and also probably marginally faster as d is local which is looked up faster than the global document.

Next we have:

var js, fjs = d.getElementsByTagName(s)[0];

This line declares a variable and finds the first available <script> element on the page. I’ll get to that in a second.

Line 3 checks whether the script isn’t already on the page and if so, exits early as there’s nothing more to do:

if (d.getElementById(id)) return;

We only need the file once. This line prevents the script file from being included several times when people copy and paste this code multiple times on the same page. This is especially bad with a regular blocking script tag because the end result is something like (assuming a blog post type of page):

<script src="...all.js"></script>
<fb:like /> <!-- one like button at the top of the blog post -->

<script src="...all.js"></script>
<fb:like/> <!-- second like like button at the end of the post -->

<script src="...all.js"></script>
<fb:comments/> <!-- comments plugin after the article -->

<script src="...all.js"></script>
<fb:recommendations/> <!-- sidebar with recommendations plugin -->

This results in a duplicate JavaScript, which is all kinds of bad (, because some browsers may end up downloading the file several times.

Even if the JavaScript is asynchronous and even if the browser is smart enough not to reparse it, it will still need to re-execute it, in which case the script overwrites itself, redefining its functions and objects again and again. Highly undesirable.

So having the script with an id like 'facebook-jssdk' which is unlikely to clash with something on the host page, lets us check if the file has already been included. If that’s not the case, we move on.

The next line creates a script element and assigns the ID so we can check for it later:

js = d.createElement(s); = id;

The following line sets the source of the script:

js.src = "//";

Note that the protocol of the URL is missing. This means that the script will be loaded using the host page’s protocol. If the host page uses http://, the script will load faster, and if the page uses https:// there will be no mixed content security prompts.

Finally, we append the newly created js element to the DOM of the host page and we’re done:

fjs.parentNode.insertBefore(js, fjs);

How does that work? Well, fjs is the first (f) JavaScript (js) element available on the page. We grabbed it earlier on line #2. We insert our new js element right before the fjs. If, let’s say, the host page has a script element right after the body, then:

  • fjs is the script.

  • fjs.parentNode is the body.

  • The new script is inserted between the body and the old script.

Appending Alternatives

Why the trouble with the whole parentNode.insertBefore? There are simpler ways to add a node to the DOM tree, like appending to the <head> or to the <body> by using appendChild(), however this is the way that is guaranteed to work in nearly all cases. Let’s see why the others fail.

Here is a common pattern:


Or a variation if document.head is available in newer browsers:

(document.head || document.getElementsByTagName('head')[0]).appendChild(js);

The problem is that you don’t control the markup of the host page. What if the page doesn’t have a head element? Will the browser create that node anyways? Turns out that most of the times, yes, but there are browsers (Opera 8, Android 1) that won’t create the head. A BrowserScope test by Steve Souders demonstrates this (

What about the body? You gotta have the body. So you should be able to do:


I created a browserscope test ( and couldn’t find a browser that will not create document.body. But there’s still the lovely “Operation Aborted” error which occurs in IE7 when the async snippet script element is nested and not a direct child of the body.

Last chance:


document.documentElement is the HTML element and its first child must be the head. Not necessarily, as it turns out. If there’s a comment following the HTML element, WebKits will give you the comment as the first child. There’s an investigation with a test case that show this (


Despite the possible alternatives, it appears that using the first available script node and insertBefore is the most resilient option. There’s always going to be at least one script node, even if that’s the script node of the snippet itself.

(Well, “always” is a strong word in web development. As @kangax ( pointed out once, you can have the snippet inside a <body onload="..."> and voila—magic!—a script without a script node.)

What’s Missing?

You may notice some things missing in this snippet that you may have seen in other code examples.

For instance there are none of:

js.async = true;
js.type = "text/javascript";
js.language = "JavaScript";

These are all defaults which don’t need to take up space, so they were omitted. Exception is the async in some earlier Firefox versions, but the script is already nonblocking and asynchronous enough anyway.

Same goes for the <script> tag itself. It’s an HTML5-valid bare-bones tag with no type or language attributes.

First Parties

This whole discussion was from the perspective of a third-party script provider. If you control the markup, some things might be different and easier. You can safely refer to the head because you know it’s there. You don’t have to check for duplicate insertions, because you’re only going to insert it once. So you may end up with something much simpler, such as:

<script>(function(d) {
  var js = d.createElement('script');
  js.src = "";
  (d.head || d.getElementsByTagName('head')[0]).appendChild(js);

This is all it takes when you control the host page.

Also we assumed all the time that whenever the script arrives, it just runs. But you may have different needs, for example call a specific function once the script is ready. In which case you need to listen to js.onload and js.onreadystatechange (example: In even more complex examples, you may want to load several scripts and guarantee their order of execution. At this point you may want to look into any of the available script loader projects such as LAB.js ( or head.js ( which are specially designed to solve these cases.

Parting Words: On the Shoulders of Giants

It’s a little disturbing that we, the web developers, need to go to all these lengths to assure an asynchronous script execution (in a third-party environment or not). One day, with a few dead browsers behind us, we’ll be able to simply say script async=true and it will just work. Meanwhile, I hope that this post will alleviate some of the pain as a resource to people who are yet to come to this problem and will hopefully save them some time.

Google AdSense folks have gone through a lot of trial and error while sharing their progress with the community, and Mathias Bynens also wrote an inspirational critique ( of their snippet. Steve Souders ( has done research and written about this topic, and was probably among the first to use such a technique for loading JavaScript. There are writeups from Yahoo and many others on the topic. These are some of the giants that have helped in the search of the “perfect” snippet. Thank you!

(Psst, and if you see something that is less than perfect in the snippet, please speak up!)


To comment on this chapter, please visit Originally published on Dec 04, 2011.

The best content for your career. Discover unlimited learning on demand for around $1/day.