AjaxPatterns
This is the original book draft version for the Scheduling Ajax pattern, offered for historical purposes and as an anti-spam measure.
You're probably better off visiting the Live Wiki Version for Scheduling instead, where you can make changes and see what others have written.

Scheduling

Cron, Event, Future, Loop, Periodic, Plan, Repeating, Schedule, Sequence, Timeout

Goal Story

Frank's panning across a map of the factory. To ensure he monitors all regions, each is colour-coded according to how recently it was investigated. It's implemented with Scheduling: After five idle minutes, the room turns orange; after ten minutes, it turns red.

Problem

How can you run actions in the future, or repeatedly?

Forces

  • Sometimes, an application needs to repeatedly run the same action, e.g. to extract new data from the server or to monitor application state.

  • Sometimes, an application needs to run an action at some future time, e.g. to warn a user their session is about to time out.

  • The server can't initiate a connection to the client, so there's no way to have the server "wake up" the client according to a schedule.

Solution

Use Javascript timers to schedule actions. Javascript's timer mechanism lets you schedule a one-off action or an action repeating at fixed intervals. In either case, what you specify is an action and a period of time in milliseconds. Note: An online demo illustrates the code concepts throughout this Solution and the code snippets loosely follow from the demo.

The naieve way to run an event in the future would be:

    sleep(5000); // Nope, won't work.
    expire();

That won't work because Javascript doesn't have a sleep() capability - you can't just block in the middle of a script[6]. Instead, you need to schedule the execution.

The most basic usage is planning a one-off event in the future. For example, an E-Commerce application wants to expire a price offer after 5 seconds:

    setTimeout(expire, 5000);
    function expire() { $("price").innerHTML = "Expired"; }

What if something happens and you want to cancel the timer. For example, the user starts typing in a deal quantity, and the E-Commerce application wants to hold off to give the user some more time. setTimeout actually returns a timer object, which allows for cancellation:

    var expiryTimer;
    ...
    expiryTimer = setTimeout(expire, 5000);
    $("dealQuantity").onkeypress = function() { // User typed something.
      clearTimeout(expiryTimer);
    };

In this example, it might make more sense to postpone, rather than cancel, expiry altogether. You can achieve this by creating a new timer:

    var expiryTimer;
    ...
    expiryTimer = setTimeout(expire, 5000);
    $("dealQuantity").onkeypress = function() { // User typed something.
      clearTimeout(expiryTimer);
      expriyTimer = setTimeout(expriyTimer, 2000); // 2 secs more after a keystroke
    };

So far, the future action has been a single function call (expire()). Sometimes, it's more convenient to say what happens as part of the timeout, in which case you can wrap it all in a string. This prevents the need to create a function specifically to handle the timeout. The string will be evaluated upon timeout:

    setTimeout("'$('price').innerHTML = 'Expired'", 5000); // Got rid of the function.

A string is also useful when you want to specify an argument, either fixed or dependent on some variable:

    setTimeout("expireWithMessage('The deal is off!')", 5000);
    setTimeout("expireWithMessage(name + ' deal is off!')", 5000);

You can pass a function instead of a string:

    setTimeout(function() {
      expireWithMessage(name + ' deal is off!'); // Caution!
    }, 5000);

That will work, but beware: the expression will evaluate at time of execution, not declaration. The name variable will resolve to the value of name when the timer fires, not when it's created. To make it more concrete, here's a script that issues two alerts. What do you think they say?

    var name = "Ye Olde DotCom";
    setTimeout("alert('Dealing with " + name + "')", 3000);
    setTimeout(function() {
      alert(name + ' deal is off!'); // Caution!
    }, 5000);
    name = "New Formula 2.0";

If you run this script at http://ajaxify.com/run/scheduling/name/, you'll see two alerts:

    Dealing with Ye Olde DotCom
    New Formula 2.0 is off

What's going on? In the first case, name is packed into a new string object when the timer's created and it's that complete string which will be used when the timer fires. In contrast, the second case involves a variable, name, which - being an ordinary Javascript variable - is a pointer to some memory location. The value in the memory location will only be looked up when the timer fires.

How, then, do you pass an argument to the scheduled function so that it will evaluate when you set up the timer rather than when the timer actually fires? The easiest way is to build up a string, as shown above, but that's ugly for a long block of code. There's another technique based on closures, illustrated in a further refactoring. It's based on an idea by Scott Isaacs; see his explanation for more details.

The second type of Scheduling is repetition, and the mechanism is almost identical to one-off event handling. Instead of setTimeout, use setInterval. Again, the call will return a timer object, useful if you want the option of canceling the loop with clearTimeout. The second argument is again a period of time, but with setInterval, it represents the loop interval. The following code will call refreshData every 5 seconds:

    setInterval(refreshData, 5000);

A common alternative is to loop with setInterval. Here, a new call to the same function is re-scheduled, usually subject to some condition. Thus, the function call is repeated until some criterion has been reached.

The timing mechanism isn't super-precise, especially when the user's running lots of programs at the same time and the browser's managing several web pages at once. If timing is important, be sure to test on the targeted browser platforms and ensure your program compensates for any lag. For instance, you might need to cut off an animation effect if periodic timestamp queries suggest it's taking too long.

Real-World Examples

Claude Hussenet's Portal

Claude Hussenet's portal (Figure 1.17, “Claude Hussenet's Portal”) shows various news information, and uses a timer to keep it fresh, as discussed in “Periodic Refresh”.

Figure 1.17. Claude Hussenet's Portal

Claude Hussenet's Portal

Google Suggest

Google Suggest offers “Suggestion”s from the server as you type a query (Figure 1.18, “Google Suggest”). Instead of submitting the query string upon each keystroke, it uses a timer to limit how many queries are sent per second. The pattern's described in “Submission Throttling”.

Figure 1.18. Google Suggest

Google Suggest

Apple ITunes Counter

As ITunes Music Store neared its 500 millionth song download, Apple decorated its homepage with a counter that appeared to show the number of downloads in real time. In reality, it was a “Guesstimate”. Every few minutes, it would grab the real sales data from the server, and also estimate how many songs are being sold per second. Between those checkpoints, it would use a timer, combined with the estimated sales rate, to continuously update the counter display.

Backpack

37Signals' Backpack maintains items in a list. When data changes, it uses a visual effect known as the "Yellow Fade Technique", where some text lights up and then fades away. As with most visual effects (see “One-Second Spotlight”, “One-Second Mutation”, “One-Second Motion”), there's a reliance on timers to co-ordinate the display across time.

Code Example [AjaxPatterns Basic Wiki]

The Basic Wiki Demo involves a “Periodic Refresh” every five seconds to synchronise with the server. The functions to start and stop the loop are encapsulated in their own functions, so as to hide timer details from the rest of the code:

    function startPeriodicSync() {
      stopPeriodicSync();
      syncTimer = setInterval(synchronise, 5000);
    }
    
    function stopPeriodicSync() {
      clearInterval(syncTimer);
    }

How are these used? Upon loading, an initial synchronisation is performed, and startPeriodicSync() called to synchronised thereafter. When the user starts typing inside a message, stopPeriodicSync is called, and the loop starts up again when the focus leaves the message area:

    window.onload = function() {
      synchronise();
      startPeriodicSync();
    }
   
    function onMessageFocus(event) {
      ...
      stopPeriodicSync();
    }
   
    function onMessageBlur(event) {
      ...
      startPeriodicSync();
    }

Alternatives

HTTP Meta Refresh

The HTTP Meta Refresh tag schedules the browser to load an entire page at some future time. It's used by conventional news portals, for example, to refresh all content every 15 minutes or so. However, it's very limited since it accepts no input from the browser and forces a complete page refresh, thus destroying all browser application state.

Metaphor

Alarm clocks perform an action at a specified time, and most also have a repetition capability allowing them to annoy their owners at the same time every day.



[6] There are some workarounds, though they're not really suitable for production.

Live Wiki Version for Scheduling