Section 7.2. Scheduling


7.2. Scheduling

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

Figure 7-3. Scheduling


7.2.1. Goal Story

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

7.2.2. Problem

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

7.2.3. 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 his 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.

7.2.4. 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 (http://ajaxify.com/run/scheduling) illustrates the code concepts throughout this section and the code snippets loosely follow from the demo.

The naïve 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( ) capabilityyou can't just block in the middle of a script.[*] Instead, you need to schedule the execution.

[*] There are some workarounds (http://www.faqts.com/knowledge_base/view.phtml/aid/1602/fid/143), though they're not really suitable for production.

The most basic usage is planning a one-off event in the future. For example, suppose an ecommerce application wants to expire a price offer after five 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);     expiryTimer = setTimeout(expiryTimer, 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. That complete string will be used when the timer fires. In contrast, the second case involves a variable, name, whichbeing an ordinary JavaScript variableis 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 (http://ajaxify.com/run/scheduling/name/). It's based on an idea by Scott Isaacs; see his explanation for more details (http://spaces.msn.com/members/siteexperts/Blog/cns!1pNcL8JwTfkkjv4gg6LkVCpw!340.entry).

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 five seconds:

   setInterval(refreshData, 5000); 

A common alternative is to loop with setInterval. Here, a new call to the same function is rescheduled, 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.

7.2.5. Real-World Examples

7.2.5.1. Claude Hussenet's Portal

Claude Hussenet's portal (http://claudehussenet.com/; see Figure 7-4) shows various news information, and uses a timer to keep it fresh, as discussed in Periodic Refresh (Chapter 10).

Figure 7-4. Claude Hussenet's portal


7.2.5.2. Google Suggest

Google Suggest (http://www.google.com/webhp?complete=1&hl=en) offers Suggestions from the server as you type a query (Figure 7-5). 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 (Chapter 10).

Figure 7-5. Google Suggest


7.2.5.3. Apple iTunes counter

As iTunes Music Store neared its 500 millionth song download, Apple decorated its homepage (http://apple.com) 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 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.

7.2.5.4. Backpack

37Signals' Backpack (http://www.backpackit.com/) 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, and One-Second Motion [Chapter 16]), there's a reliance on timers to coordinate the display across time.

7.2.6. Code Example: AjaxPatterns Basic Wiki

The Periodic Refresh Basic Wiki Demo (http://ajaxify.com/run/wiki) involves a loop to synchronize 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 synchronization is performed, and startPeriodicSync( ) is called to synchronize 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( );   } 

7.2.7. Alternatives

7.2.7.1. 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 states.

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




Ajax Design Patterns
Ajax Design Patterns
ISBN: 0596101805
EAN: 2147483647
Year: 2007
Pages: 169

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net