
Download, Incremental, Multi-Stage, Parallel
Dave's been informed an E-Commerce homepage is turning many users away with its absurdly slow initial download. He refactors it so the initial download is nothing more than a banner and the key navigation links. News and featured items are still on the page, but they're pulled down separately, after the initial page has loaded.
Content can be time-consuming to load, especially when it contains rich images.
Users rarely need to read all the content on the page.
Users often have to click through several times to locate a particular item.
Users sometimes browse around by exploring links between pages.
Break content download into multiple stages, so that faster and more important content will arrive first.. Typically, the page is divided into placeholders (e.g. div elements) with the content for each placeholder downloaded independently. “XMLHttpRequest Call”s can be issued simultaneously or in a serial sequence, and the page will gradually be populated as the calls return.
The pattern applies when an application needs to download a lot of content from the server. Typically, this occurs on startup and also when a major context switch occurs. By breaking up the call, you can deliver faster and more important data earlier on. You avoid bottlenecks that occur when a single piece of content blocks everything else from returning.
The initial download of a page might be very small, containing just a skeleton along with navigation links and enough context to help the user decide if it's worth sticking around. That's the critical content for a fresh visitor, and there's no need to make them wait for the rest of the page to load. The browser can request the main page content as a separate call.
This pattern is in some respects an Ajaxian upgrade of the old technique of filling up a page gradually, where the server continues flushing the buffer and the user can see the page as it loads. Ajax makes the whole thing more powerful, because the latter downloads can attach to any existing part of the page.
One resource risk is having too many requests at once. Thus, consider a (purely speculative) "Multiplex Call" variant: Establish a browser-server protocol that lets the browser wrap multiple requests and the server wrap multiple responses. Then, only issue a single call with all required data inside. The immediate response might only return a few results, so the browser waits a bit, then makes a further request for outstanding data. The whole process repeats until the browser has all data.
The trickiest decision is how to divide the page into blocks, each of which will be downloaded individually. Since the blocks are downloaded in parallel, the main advice is to create small blocks of initial content to ensure the initial download is quick, and to group together content that's likely to be ready at the same time. Also, too many blocks will give the initial load an ugly appearance, and may have the undesirable effect of causing already-displayed elements to move around. For a fresh visitor, one example would be to create blocks as follows:
A block of general information about the website, e.g. name, summary, a small icon.
A navigation block showing links within the site.
A block of general information about the current page, such as a heading and summary text. In a web app, this may be dynamic information based on what processing is being performed.
One or more blocks of main page content.
One or more blocks of auxiliary information, e.g. cross-site promotions, advertising, legal notices.
Some of these may well be combined too, as it's important to avoid too many blocks. So you might have a single request to retrieve both navigation and general information, which is then split and painted onto separate blocks.
Note that this doesn't have to apply to a homepage - if you are offering “Unique URLs”, visitors can jump into an Ajax App at different points, so the main page content might actually be quite "deep" into the application.
Ideally, this pattern should not affect the page appearance, but will require some thinking about the underlying HTML. In particular:
As new information loads, it's possible the browser application will automatically rearrange the page in order to accommodate new information. This can lead to the user clicking on the wrong thing, or typing into the wrong area, as items suddenly jump around the page. Ideally, information will not move around once it has been presented to the page. At least for images, it's worthwhile defining the width and height in advance, as CSS properties.
CSS-based layout is likely to play a big part in any solution, as it provides the ability to exercise control over layout without knowing exact details.
The entire DOM itself can be established on the initial load, so that divs and other structures are used as placeholders for incoming content. If that's the case, most of those elements can be left alone, but if the user is waiting for something specific, and it may take some time, it would be worth placing a “Progress Indicator” on the block where the output will eventually appear.
It's also possible to construct the DOM dynamically, so that new elements are added as new content arrives. This approach may be more work, but may help to decouple the browser and the server. With the right framework in place, it means that the browser does not have to know exactly which items will be downloaded. The browser might ask the server to send all adverts, and the sender simply responds by sending down an XML file with three separate entries, which the browser then renders by adding three new divs.
Let's say you're at a point where you suddenly need a whole lot of content. Should you issue several requests at once? Not necessarily. Keep in mind there are limits on how many outgoing requests the browser can handle at one time and consider what's best for the user. If there's a bunch of content that might not be used, consider deferring it for a bit with a Javascript setTimeout call. That way, you can help ensure the user's bandwidth is dedicated to pulling down the important stuff first.
Kayak is a travel search engine. You tell it where you're going, and it then searches through many online travel sites to build up a set of options for you.
While waiting for a search query, the results page initially shows just the site banner, the search you entered, and a “Progress Indicator” (Figure 1.41, “Kayak Progress Indicator”). As the results for each external site arrives, Kayak adds the site name and whatever flights it found there. The flights aren't just appended, but kept in sorted order. You also have the option to finish the search by clicking an Enough Results button.
NetVibes is an Ajax portal that launches parallel “XMLHttpRequest Call”s on startup, one for each “Portlet”. Many portals follow this pattern.
TalkDigger is an Ajaxian meta-search for web feeds. Traditional meta-searches usually output results to the browser as they're received from individual engines, so the results page isn't well-ordered. With TalkDigger, each individual search engine gets its own div on the page. As the results come in, they're placed on the right spot.
The code example is a portal demo which downloads content in various stages (Figure 1.42, “Multi-Stage Download Portal Demo”):
The page banner and key links are downloaded immediately, as part of the initial HTML.
On page load, a place is set aside for side links, which are then requested and injected into the placeholder.
Likewise, the main content is also requested on page load.
Finally, an ad is requested after a short delay. This ensures the main content arrives first, so the ad doesn't affect the overall flow. (If you're feeling entrepreneurial, you can refactor it to reverse the order.)
The initial HTML contains the header and placeholders for the content which will be injected. There's an initial "Loading" message for each of these:
<h1>ajax patterns Portal demo</h1>
<a href="http://ajaxpatterns.org">Ajax Patterns Wiki</a> |
<a href="http://ajaxify.com">Ajax Demos</a> |
<a href="http://ajaxpatterns.org/Ajax_Examples">Ajax Examples</a>
<div class="spacer"> </div>
<div id="leftLinks">
<div id="siteLinks">Loading ...</div>
<div class="spacer"> </div>
<div id="ad">Loading ...</div>
</div>
<div id="allPatterns">Loading ...</div>
The side links and main content are loaded immediately on page loaded, and sent to a callback function which morphs the corresponding div:
window.onload = function() {
ajaxCaller.get("sideLinks.html", null,
onServerResponse, false, "siteLinks");
ajaxCaller.get("allPatterns.phtml", null,
onServerResponse, false, "allPatterns");
...
}
function onServerResponse(html, headers, elementId) {
$(elementId).innerHTML = html;
}
A few seconds later - probably after all the content has been loaded - the ad content is requested:
window.onload = function() {
ajaxCaller.get("sideLinks.html", null,
onServerResponse, false, "siteLinks");
ajaxCaller.get("allPatterns.phtml", null,
onServerResponse, false, "allPatterns");
setTimeout("ajaxCaller.get('ad.html',null,onServerResponse,false,'ad')", 5000);
}
“Portlet”s are good candidates for content-specific downloads. A portal's overall structure can be downloaded initially, and each portlet's content then be downloaded (and refreshed) in parallel to the rest of the page.
While a block is waiting to be loaded, you might populate it temporarily with a “Guesstimate”.
While a block is waiting to be loaded, you might populate it temporarily with a “Progress Indicator”.
Like Multi-Stage Download, “On-Demand Javascript” involves an initial download followed by further downloads later on. That pattern focuses specifically on Javascript content rather than display and semantic content. It's also about downloading only when needed. The emphasis here is on downloading according to an initial schedule. Other patterns, such as “Microlink” and “Live Search”, cover on-demand downloading of display and semantic content.