Gmail for Mobile HTML5 Series: Reducing Startup Latency
    
    
    
    
    On April 7th, Google launched a new version of Gmail for mobile
      for iPhone and Android-powered devices. We shared the behind-the-scenes story through this
      blog and decided to share more of what we've learned in a brief series of follow-up
      blog posts. This week, I'll talk about how modularization can be used to greatly reduce the
      startup latency of a web app.To a user, the startup latency
      of an HTML 5 based application is critical. It is their first impression of the application's
      performance. If it's really slow, they might not even bother to wait for the app to load
      before 
navigating
      away. Even if your application is blazing fast after it loads, the user may never
      get the chance to experience it.
There are several aspects of an HTML 5
      based application that contribute to startup latency:
- Network
      time to fetch the application (JavaScript + HTML)
 - JavaScript parse
      time
 - Code execution time to fetch the data and render the home page of
      your application
 
The third issue is up to you! The first two issues,
      however, are directly correlated with the size of the application. This is a tricky problem
      since as your application matures, it will have more features and the code size will get
      bigger. So, what to do? Modularize your application! Split up your code into independent,
      standalone modules. Consider splitting each view/screen of your application and implement each
      new feature as its own module. This is only half the story. Now that you have your code
      modularized, you need to decide which subset of these modules are critical to load your
      application's home page. All the non-core modules should be downloaded and parsed at a later
      time. With a consistent code size for your startup code, you can maintain a consistent startup
      time. Now, let's go into some nitty gritty details of how we built an application with
      lazy-loaded modules.
How to Split Your Code into
      ModulesSplitting an application into individual modules
      might not be as simple as you think. Code that serves a common purpose/functionality should be
      grouped together and form a module (comparable to a library). As mentioned earlier, we
      selected which modules are critical to the home page of the app and which modules can be
      lazy-loaded at a later time. Let's use a Weather application as an example:
High Level Functionality:- A "Weather in
      my Favourite Cities" home page
 - Click on a city to view the cities entire
      week forecast
 - Weather data comes from an external web
      service
 
Possible Module Separation:- Weather data model
 - Weather web service
      API
 - Common UI widgets (buttons, toolbars, navigation,
      etc)
 - Favourite Cities page
 - City Weather Forecast
      page
 
Now let's say your users want a "breaking news" feature. No problem:
      just put the page, the news data API and the data model into a new module.
One thing to keep in mind is the dependency order of your modules. For modules that have
      many downstream dependencies, it might make sense to include them as part of the core
      modules.
How to Lazy Load the ModulesOption 1: Script as DOMThis method
      uses JavaScript to insert SCRIPT tags into the HEAD's DOM.
<script type="text/JavaScript">
  function loadFile(url) {
    var script
      = document.createElement('SCRIPT');
    script.src =
      url;
   
      document.getElementsByTagName('HEAD')[0].appendChild(script);
  }
</script>
Option 2: XmlHttpRequest
      (XHR)This method sets up XmlHttpRequests to retrieve the
      JavaScript . The returned string should be evaluated in the XHR callbacks (using the
      eval(string) method). This method is a little more complicated but it gives you more control
      over error handling.
<script
      type="text/JavaScript">
  function loadFile(url) {
     function callback() {
      if (req.readyState == 4) { // 4 =
      Loaded
        if
      (req.status == 200) {
         
      eval(req.responseText);
        } else {
         
      // Error
       
      }
      }
    };
    var req = new
      XMLHttpRequest();
    req.onreadystatechange =
      callback;
    req.open("GET", url, true);
    req.send("");
  }
</script>The next question is, when to lazy load
      the modules? One strategy is to lazy load the modules in the background once the home page has
      been loaded. This approach has some drawbacks. First, JavaScript execution in the browser is
      single threaded. So while you are loading the modules in the background, the rest of your app
      becomes non-responsive to user actions while the modules load. Second, it's very difficult to
      decide when, and in what order, to load the modules. What if a user tries to access a
      feature/page you have yet to lazy load in the background? A better strategy is to associate
      the loading of a module with a user's action. Typically, user actions are associated with an
      invocation of an asynchronous function (for example, an onclick handler). This is the perfect
      time for you to lazy load the module since the code will have to be fetched over the network.
      If mobile networks are slow, you can adopt a strategy where you prefetch the code of the
      modules in advance and keep them stored in the javascript heap. Only then parse and load the
      corresponding module on user action. One word of caution is that you should make sure your
      prefetching strategy doesn't impact the user's experience - for example, don't prefetch all
      the modules while you are fetching user data. Remember, dividing up the latency has far better
      for users than bunching it all together during startup. 
For an HTML 5
      application that takes advantage of the application cache to reduce startup latency and to
      serve the application offline, there are a few caveats one should be aware of. Mobile networks
      have decent bandwidth, but poor round trip latency, so listing each module as a separate
      resource in the manifest incurs quite a bit of extra startup latency when the application
      cache is empty. Also, if one of the module resources fails to be downloaded by the application
      cache (e.g. disconnected from network), additional error handling code needs to be written to
      handle such a case. Finally, applications today have no control when the application cache
      decides to download the resources in the manifest (such a feature is not defined in the
      current specification of the draft standard). Typically, resources are downloaded once the
      main page is loaded, but that's not an ideal time since that's when the application requests
      user data.
To work-around these caveats, we found a trick that allows
      you to bundle all of your modules into a single resource without having to parse any of the
      JavaScript. Of course, with this strategy, there is greater latency with the initial download
      of the single resource (since it has all your JavaScript modules), but once the resource is
      stored in the browser's application cache, this issue becomes much less of a factor.
To combine all modules into a single resource, we wrote each module into a
      separate script tag and hid the code inside a comment block (/* */). When the resource first
      loads, none of the code is parsed since it is commented out. To load a module, find the DOM
      element for the corresponding script tag, strip out the comment block, and eval() the code. If
      the web app supports XHTML, this trick is even more elegant as the modules can be hidden
      inside a CDATA tag instead of a script tag. An added bonus is the ability to lazy load your
      modules synchronously since there's no longer a need to fetch the modules asynchronously over
      the network.
On an iPhone 2.2 device, 200k of JavaScript held within a
      block comment adds 240ms during page load, whereas 200k of JavaScript that is parsed during
      page load added 2600 ms. That's more than a 10x reduction in startup latency by eliminating
      200k of unneeded JavaScript during page load! Take a look at the code sample below to see how
      this is done. 
<html>
...
<script id="lazy">
// Make sure you strip out (or replace)
      comment blocks in your JavaScript first.
/*
JavaScript of lazy
      module
*/
</script>
<script>
  function lazyLoad() {
    var lazyElement = document.getElementById('lazy');
    var lazyElementBody = lazyElement.innerHTML;
    var jsCode = stripOutCommentBlock(lazyElementBody);
    eval(jsCode);
  }
</script>
<div onclick=lazyLoad()>
      Lazy Load </div>
</html>
In the
      future, we hope that the HTML5 standard will allow more control over when the application
      cache should download resources in the manifest, since using comments to pass along code is
      not elegant but worked nicely for us. In addition, the snippets of code are not meant to be a
      reference implementation and one should consider many additional optimizations such as
      stripping white space and compiling the JavaScript to make its parsing and execution faster.
      To learn more about web performance, get tips and tricks to improve the speed of your web
      applications and to download tools, please visit 
http://code.google.com/speed.
Previous posts from Gmail for Mobile HTML5 SeriesHTML5 and Webkit pave the way for mobile web applications
 Using
      AppCache to launch offline - Part 1
 Using AppCache to launch offline - Part
      2
 Using
      AppCache to launch offline - Part 3
 A Common API for Web Storage
      Suggestions for
      better performance
 Cache pattern for offline HTML5 web
      applicationBy Bikin Chiu, Software Engineer, Google Mobile