Sunday, August 15, 2010

JS Performance and YUI3

Javascript performance in the browser is a pretty important usability issue we have to deal with every day. The problem starts from the fact that javascript is blocking, meaning that nothing else can happen while javascript code is being executed. Browsers use a single thread to execute JS code and update the UI. Whether we include a script tag within the body of our page or in an external file, the browser will stop rendering the page and it will wait to for the script to complete. Two good techniques to improve our page's performance are:
  • grouping scripts as much as possible
  • loading JS code after the page has finished loading (aka nonblocking scripts)
A good example to look at these techniques is the YUI library. Recently I worked with YUI3 which is, I think a pretty cool library. Let's see how it works:
Grouping Scripts
Every time we include an external JS file in our HTML, an HTTP request is issued, which by itself causes an additional performance penalty. Yahoo created the combo handler to distribute the YUI library files. YUI's users can pull any number of files by using a combo-handled URL, such as: "http://yui.yahooapis.com/combo?3.1.0/build/oop/oop-min.js&3.1.0/build/dom/dom-min.js&3.1.0/build/event-custom/event-custom-min.js&3.1.0/build/event/event-base-min.js". This URL includes 4 different files and there is no need to issue 4 different HTTP requests, we can just fetch all these files with one HTTP request. Pretty cool, right?
Nonblocking Scripts
YUI uses the concept of a small initial code on the page followed by downloading additional functionality later. To use YUI3 in our page we only need to include the YUI seed file:
<script type="text/javascript" src="yui-min.js"/>
The seed file is about 10KB (6KB gzipped) and includes enough functionality to download any other YUI components in the Yahoo CDN (content delivery network). For example, if we want to use the DOM utility, we just need to specify its name within the YUI use() method. The use method will provide a callback that will be executed when the code is ready, an example piece of code is:
YUI.use("dom", function(Y) {
   var testElmt = Y.one('#test123'); //gets element by id
   // do something with the element
});
This code creates a new instance of the YUI object and then calls the use() method. The seed file has all the information about filenames and dependencies, so specifying "dom" actually builds up a combo-habdler URL with all of the correct dependency files and creates a dynamic script element to download and execute those files. When all of the code is available the callback method is called and the YUI instance is passed in as the argument, allowing to immediately start using the newly downloaded functionality. The good thing with nonblocking scripts is that the JS files are downloaded as soon as the element is added in the page, so we don't need to actually block any other process in it.





References: High Performance JavaScript, Nicholas C. Zakas (http://oreilly.com/catalog/9780596802806)

2 comments:

  1. How does YUI's library grouping affect caching? I would guess that your browser has to cache each fully formed HTTP request separately and thus for every combination of libraries that you may be loading you lose an opportunity to skip the request all together.

    I'm sure in the long run its worth it, eventually the browser will cache the various combinations that your scripts are loading but that could be up to n! possible combinations where n is the number of libraries you include.

    ReplyDelete
  2. @Julian hmmm...yep thats an interesting point. Never thought of it. Its kind of a trade off...but still I think I would lean towards YUI's tactic.

    The thing is that the JS files size is really small, e.g take a look at here: http://yui.yahooapis.com/combo?3.1.0/build/oop/oop-min.js, this file is 2.28K unzipped (=1.1K gzipped), if you combine 3 or even more files like this one, the overhead of downloading one such file will still be way less than issuing 3 (or more) HTTP requests for each file.

    ReplyDelete