YUI 3 AsyncQueue + Cache Warmer = better Yahoo! Mail performance

JavaScript runs the race on a single thread. In a recent talk about Responsive Interfaces, Nicholas Zakas describes how the single-threaded execution time is split between the JavaScript thread and the UI thread. To make a web application feel quick and snappy, 50-100 milliseconds is all the time we have to give the user feedback about their interaction. The AsyncQueue implementation in YUI 3 is a way to enable progressive incremental rendering of an application, so that user can start interacting even while the infrastructure is being set up.

To make a responsive web application, high-execution processes are broken into small chunks and queued up. The Yahoo! Mail cache warmer module uses the browser/application idle time to pre-fetch some important components and data. The bulk of the wait time for an internet application is spent bringing information across the wire. Once it reaches the browser, information can be cached for immediate or future use.

Cache warmer sequence for preparing compose dependenciesCache warmer sequence for preparing compose dependencies

Users of Yahoo! Mail follow two interaction paths: dynamic and passive interaction. A dynamic interaction phase is when the application and browser are busy responding to user actions like click, drag, download, etc. A passive interaction phase is when the user is not expecting any feedback. A passive interaction gives us a slot in the application interaction timeline called "idle" time. The idle time is particularly interesting because it gives us an opportunity to anticipate the user's next move and prepare the application for that. The cache warmer class is a facade over YUI's AsyncQueue.

The most important method in the cache warmer is the idle check method. The idle check method looks for any download activity and interaction activity. If nothing is happening in the browser, the cache warmer starts putting things in the queue. Consider the timeline where a user is reading a long email. There is a high probability that the next action will be a reply to that email. During this idle time, the modules needed for the "compose" interaction are loaded into the queue so that dependencies for the "compose" module are all updated into cache even before the user actually clicks on "reply."

Cache warmer is also used is to pre-fetch email messages. Unlike other email clients that pre-fetch every message in the inbox, we try to make smarter choices when we pre-fetch. For example, a user is more likely to read an email message from a contact already in their address book than from a unknown user. Each of the message criteria is assigned a score, and each message is given a rank. During idle time, messages with the highest rank are pre-fetched and updated into the cache. This way, when the user is clicking on the message, there is a high probability that the message is already in the message cache.

The AsyncQueue implementation works well for us because it guarantees the execution order of the jobs in the queue when the queue is run. Re-flows to display DOM modifications incrementally take place while batches of work are being done in JavaScript. If there is a operation that requires the queue to be stopped, the stop() method will be called to stop execution and clear all the contents of the queue.

   var composeDependencies = [
  cachewarmer.warmUp("compose", composeDependencies);

Senthil Padmanabhan from the Yahoo! Mail performance engineering team was the brains behind the cache warmer. He continues to work towards keeping Yahoo! ahead on performance.