Predictive Fetch
From Ajax Patterns
There's an archived version of this pattern available, taken from the Ajax Pattern book draft, showing roughly how it appeared before the page became publicly editable.
Evidence: 1/3
Tags: Fetch Anticipate Preload Prefetch Guess
Contents |
Ajax/Javascript Programming and Usability in "Ajax Design Patterns" Book |
In A Blink
TODO Diagram: Screen showing portion of map with dashed outline of anticipated next move (a bigger outside box)
Goal Story
Tracy is editing an online spreadsheet and sets a cell to show the "sum" for the entire column. She sees the sum instantaneously, because the server has already anticipated this action and pulled down a sum of each columns into a browser-based cache.
Problem
How can you make the system respond quickly to user activity?
Forces
- The application should respond to user actions quickly, ideally instantaneously.
- Many user actions require a response from the server.
- Responses from the server can be noticeably latent due to data transfer and server processing overheads.
Solution
Have the browser anticipate likely user actions and call the server in preparation.
At any point in an Ajaxian application's lifetime, the user has the option to perform a number of activities. At the very least, there are always two things the user could do in the next moment: (a) close the application (by quitting the browser or closing the application window/tab); (b) nothing. And of course, there are usually a multitude of application-specific activities that can occur at any time.
Some of these user activities will trigger a remote scripting call to the server before returning for browser-side processing, and the delay will usually be noticeable. That's unfortunate for usability, because responses should ideally feel as if they are instantaneous. The computer not being able to freeze time, the response will never really be instantaneous. But from a usability perspective, it should ideally give that impression. To provide results at a pace that is below the level of human consciousness, a typical guideline is that the application should respond in under 10 milliseconds. That's a hard ask when a trip to the server is required. Especially in a global context, where a trip to the sever and back might cost a second or more due to network overheads.
The obvious motivation for instantaneous feedback is throughput: the user can achieve more because they're not waiting as long for results. Actually, that's not such a major issue: what's a few seconds when you're working on a document for half-an-hour?
Often, the biggest downside of latency is the distraction it causes. The user is focused on a task, and may well be in a state of "flow". A delay will cause a loss of concentration and, in addition to the time it takes to get back to this train of mind, there's also a risk that an idea might be lost forever.
Another problem is simply user frustration. Users generally like to feel in control of a system, so it's not ideal when the system causes delays, especially unpredictable delays.
A final issue with latency is its effect on responsivenes. When users are making decisions in real-time, they need real-time data. For an application such as financial trading, even a consistent 100-millisecond lead can make a big impact on overall performance.
Pre-fetching attempts to remove the delay altogether for certain user actions. It's usually infeasible to pre-fetch results for all actions, so the designer must be judicious in anticipating actions. We are especially interested in actions which are likely to be performed, so that the probability of no response is high. Furthermore, even unlikely actions may also be worth pre-fetching for, if they are critical to the application. Imagine an emergency response system, where the choices are "View your preferences", "Work on reports", and "Dispatch emergency units". The last of these might not occur very often, but which one would you like to be prefetched?
Another downside of pre-fetching is erratic behaviour. A good usability principle is consistent behaviour, but with Predictive Fetch, the user may be surpised that some commands respond instantaneously, while virtually identical commands take a long time. To make the waiting time more comfortable, consider showing a Progress Inidicator.
Decisions
How much information will be pre-fetched?
Pre-fetching comes at a cost. Anticipating the user's actions is a guessing game, and for each guess that goes wrong, some resource has been wasted. Designers must make a trade-off involving the likelihood that pre-fetched data will be used, the user benefit if it is used, and the overhead if it's not used. This could involve some fairly heavy user analysis combined with statistical methods.
In practice, it's feasible to operate on a more empirical basis. With the right design, it should be easy enough to discriminately turn pre-fetching on and off. Thus, by studying logs and comparing effects of pre-fetching different aspects, it's possible to evolve the algorithms being used.
Will it be the server or the browser that anticipates user actions?
The request for information will always come from the browser, but it's feasible for either the server or the browser to anticipate what the user will need next. If the browser is to decide, it can simply issues a request for that information. If the server is to decide, the browser can, for example, issue a periodic request for general information - perhaps with some indication of current state - and the server can then push down whatever information it decides might come in handy for the browser.
It's feasible to do it in the server or the browser, but which is more appropriate. This will likely come down to the overall application attitude: browser-centric versus server-centric.
What information can be used to anticipate user actions?
It's likely you'll use some information about the present and the past to help predict the future. There's a lot of information you can potentially use to help decide what the user will do next:
- User's Profile: The user's profile and history should provide strong cues. If the user always visits the "Books" area as soon as they log on, then the homepage should pull down "Books" content.
- User's Current Activity: It's often possible to predict what the user will do next from their current activity. If a user has just added an item to their shopping cart, they will likely be conducting a purchase in the not-too-distant future; consider downloading their previous address details.
- Activity of Other Users: Sometimes, a rush of users will do the same thnig at once. If the user has just logged into a news portal while a major news event is in progress, system-wide statistics will inform the server that this user is proabably about to click on a particular article.
- Collaborative Filtering: As an extension of the previous point, a more sophisticated technique is to correlate users based on information such as their profile and history. People are likely to behave similarly to those whose details are highly correlated. So if people like myself tend to look at "Blogs" followed by "Weather", then the system should pre-fetch "Weather" while I'm looking at "Blogs".
Real-World Examples
Google Maps
Some experimentation with Google Maps suggests Predictive Fetch is used while navigating the map. The evidence is that you can slowly move along the map in any direction, and you won't see any part of the page going blank and refreshing itself. If you move quickly, you'll see the refresh behaviour. Based on that observation, the map is apparently fetching content beyond the viewable area, in anticipation of further navigation.
Authenteo
Authenteo utilizes an alternative form of predictive fetching in which the server will respond to requests with extra data that the server expects the client will need soon. Authenteo provides client side access to the persisted objects in the application data domain model. These objects are retrieved from the server as needed. However, the Authenteo server monitors object requests, and when a request for object A is quickly followed by request for object B, the server will learn from this and provide persisted objects A and B in response to future requests for object A. The client can ingest and cache all these objects from the server and therefore the connection becomes self-optimized for minimal requests.
map.search.ch
map.search.ch contains familiar buttons for zooming and panning. What's different here is that at as soon as a mouse pointer hovers over a button, a call is made to anticipate the user clicking on it. This is a good compromise between downloading in every possible direction and zoom level, and doing nothing at all.
Firefox "Prefetch"
This is not entirely an Ajaxian example, but it's worthwhile noting a particular Firefox (and Mozilla) feature called "prefetching". The HTTP protocol allows for new link types to be defined, and Firefox happens to define a "Prefetch" link type. A "prefetch" link looks like this:
<link rel="prefetch" href="/images/big.jpeg">
When firefox sees such a link appear, it will generally fetch the associated content, ready to be shown immediately. An application can exploit that feature by including links to content that is soon likely to be request.
Google Search, for example, slaps a prefetch directive around the first search result, but they state this occurs for only for "some searches". I am guessing, based on some experimentation, that this means searches where Google is highly confident the top search will be chosen. So a search for the highly ambiguous term, "anything", results in no prefetch directive. Meanwhile, a search for IBM, where it's obvious what most users seek, will direct Firefox to prefetch the first result:
<link rel="prefetch" href="http://www.ibm.com/">
International Herald Tribunal
Another example is The International Herald Tribune, which caches entire articles to provide immediate gratification when you click on "Next Page" (an example taken from here).
Refactoring Illustration
The Basic Sum demo allows the user to enter a set of figures, and they must wait for the server in ordet to see the sum. The Sum Cached demo improves on the situation a bit, by ensuring recent sums do not need to be re-fetched. However, there is still no anticipation of user behaviour. In this refactoring, a new sum is fetched each time the user types something. By the time they've hit the Add button, the browser should hopefully have the result waiting in the cache, ready to be displayed immediately. Each time a new sum is anticipated, the browser fetches the result and a callback pushes it into the cache.
But this leaves a question: how does the callback function know whether to morph the display? In the initial version, it's always morphed because the callback assumes it was the Add button that triggered a server response. But now, there are two possible reasons: the Add button and the predictive fetch. In both cases, it will push the result to the cache. But will it morph the display? The callback function must determine what triggered the server call.
One solution would be to tell the server what kind of sum its performing. So, along with the figures, upload a flag to say if this is a predictive fetch. That's rather messy, because it wrecks the coherence of the basic sum service. A better solution is to track this flag in the browser, and that's exactly what the AjaxCaller's callingContext achieves. The callingContext effectively tracks each call to the server - it's set when the call goes out and passed to the callback function when the call returns, though it's never actually sent anywhere itself. Each time a sum is requested, callingContext is set according to whether it's a predictive fetch or not. Then, the sum response handler can ensure the result is only morphed if the sum was in response to a user request.
The code change is actually fairly straightforward. As before, clicking on the Add button causes a server sum request. This time, though, a flag is set to indicate its a user request.
$("addButton").onclick = function() {
submitSum(true);
}
For Predictive Fetch, each release of a key triggers a sum submission, with callback false to inidicate this was not a user request.
$("figure1").onkeyup = function() { submitSum(false); }
$("figure2").onkeyup = function() { submitSum(false); }
$("figure3").onkeyup = function() { submitSum(false); }
submitSum() still performs the same request, but this time passes in the flag as a calling context, so it will be retained within the browser. There is also a cosmetic change: the current sum value will only be rendered invalid if the request was issued by the user:
if (isUserSubmitted) {
repaintSum("---");
}
ajaxCaller.get("sumXML.php", figures, onSumResponse, true, isUserSubmitted);
The isUserSubmitted flag is then retrieved by the callback function, and used to decide whether to morph the sum display. Either way, the result is pushed into the cache for later retrieval.
function onSumResponse(xml, headers, callingContext) {
var isUserSubmitted = callingContext;
...
if (isUserSubmitted) {
repaintSum(sum);
}
...
}
Alternatives
Fat Client
Sometimes, the server is invoked to perform some logical processing, rather than to access server-side data. If that's the case, it may be possible to process in Javascript instead, as suggested by Fat Client.
Notifying server without pre-fetching
In some cases, the reverse may be more appropriate: it might make sense for the browser to hint to the server what the user is working on, without the server actually responding. How is this useful? It allows the server to get started on processing, so that the required information can be cached server-side, but not actually pushed into the browser's own cache.
Related Patterns
Guesstimate
Guesstimate is another performance optimisation based on probabilistic assumptions. A Guesstimate involves the browser guessing the current server state, whereas a Predictive Fetch involves guessing what the user will do next.
Browser-Side Cache
Browser-Side Cache is a prerequisite to Predictive Fetch - the predictively fetched data has to be stored in some form of cache.
Visual Metaphor
During a football game, the coach's assistant constantly produces real-time reports on match activity in case the coach demands certain information. With a knowledge of both the domain and the coach's style, those reports will be able to satisfy at least some of the coach's demands forthwith.
Time your website with
WebWait - from the creator of AjaxPatterns.org
