Mobile Gmail and async script loading

Mobile is the current web frontier where we’ll see the greatest growth in users and companies over the next few years. In case you didn’t see the announcement, Dion Almaer and Ben Galbraith just joined Palm to head up their developer relations program. At this week’s Velocity kick-off meeting, mobile performance was highlighted as a primary focus for next year’s conference.

With mobile on my mind, I was blown away by the awesome performance tips in this blog post: Gmail for Mobile HTML5 Series: Reducing Startup Latency. This post hits on the main point of my recent book – the impact of loading JavaScript. This is the #1 performance issue for today’s web apps. The problem is even worse for mobile devices where download times can be significantly worse than on the desktop.

One of the best practices I evangelize is splitting the initial payload. The Google Mobile team echoes this advice, recommending that fast mobile web apps must separate their code into modules that are critical to page startup versus modules that can be lazy-loaded. It’s important to carefully consider when to lazy-load this additional JavaScript.

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.

Loading JavaScript in the background does indeed freeze the UI and lockout the user. Even worse, they don’t know why this is happening. They didn’t invoke any action – the lazy-load was kicked off in the background. But, I’ve seen web apps adopt this recommendation of loading modules when the user requests the additional functionality, and that’s not pretty either. Waiting for a script to download, especially over mobile connections, injects too much of a delay. But the Google Mobile team found an ingenious workaround:

…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.

Your parents were wrong – you can have your cake and eat it, too! Commenting out the JavaScript code avoids locking up the browser, so the actual download can happen in the background without affecting the user experience. Once the code arrives, modules are eval’ed on an as-needed basis, without the delay of actually downloading the script. The eval does take time, but this is minimal and is tied to the user’s actions, so it makes sense from the user’s perspective.

I’ve been working on ways to download scripts asynchronously for two years. I’ve never seen this technique before. It’s very promising, and I hope to explore a few enhancements. It sounds like Google Mobile did the download as an HTML document in an iframe (“each module in a separate script tag”). This means it would have to be downloaded from the same domain as the main page – something common at Google but less typical on other web sites using CDNs or Google AJAX Libraries API, or sharding resources across multiple domains. It would be nice if each module could be a standalone script with the code commented out. This would avoid the same domain restrictions, and be faster since the scripts could be downloaded in parallel.

Hats off to the Google Mobile team. And thanks for sharing!