Welcome!

AJAX & REA Authors: Andreas Grabner, Carmen Gonzalez, Brad Abrams, Deborah Strickland, John Savageau

Related Topics: AJAX & REA

AJAX & REA: Article

Real-World AJAX Book Preview: Advanced Techniques

Real-World AJAX Book Preview: Advanced Techniques

This content is reprinted from Real-World AJAX: Secrets of the Masters published by SYS-CON Books. To order the entire book now along with companion DVDs for the special pre-order price, click here for more information. Aimed at everyone from enterprise developers to self-taught scripters, Real-World AJAX: Secrets of the Masters is the perfect book for anyone who wants to start developing AJAX applications.

Advanced Techniques
Multiple Requests/Responses in a Single Call
The great advantage of AJAX clients is that they can communicate back to the server without interrupting what the user is doing. This in turn provides the freedom for AJAX clients to talk back to the server far more frequently than in a traditional page-based Web application. This can be exploited to provide a much richer user experience, for example, by providing real-time data updates, "live search" functionality, or validating user input on-the-fly.

A result of this frequent communication is that a Web server in an AJAX application will have to handle many more requests than in a typical Web app. In a traditional Web app, the server provides a page to the user, who probably spends 10-20 seconds filling in a form before submitting it back. An AJAX client that validates every user input on-the-fly may end up sending a request to the server every second or so. This doesn't pose a problem when there's only one user, but a carelessly developed AJAX client in a large-scale deployment may result in many concurrent hits that overwhelm the server.

In this section, we're going to create just such a "chatty" AJAX client, and then work through some steps to reduce the number of calls it makes to the server.

Figure 6.13 shows a simple Web page that will serve as the basis for this scenario. It's simply 26 input elements, one for each letter of the alphabet. Each input has an ID corresponding with the letter it represents, from "a-input" to "z-input." There's also a status span element alongside each input, with IDs running from "a-status" to "z-status." The status spans are all empty to begin with.

This form simulates a dense date-entry screen in a real application. Server-side validation is applied to each of the fields, with the simple rule that a field is valid if it contains an English dictionary word beginning with the correct letter. Of course, in the real world, validation requirements are usually more complex.

Let's look at the client-side code first. When the page loads, we loop over all of the form inputs and attach onchange event-handler functions to them:

window.onload = function() {
   // Find all inputs inside the "letters" element
   var inputs = $("letters").getElementsByTagName("input");
   for (var i = 0; i < inputs.length ; i++) {

     // Attach event handler function
     inputs[i].onchange = handleChange;
   }
}

The handleChange() function simply calls another function named validate() and passes in the element that the change event fired on:

handleChange = function() {

     // Asynchronously validate input
     validate(this);
}

The validate() function takes the value of the input element, and the letter that the word should start with and creates a query parameter string from them. These parameters are then sent in an AJAX request to a PHP page, checkword.php:

validate = function(element) {

   // Figure out correct first letter from the
   // input's ID.
   var letter = element.id.substring(0,1);

   // Create query parameters
   var params = [ "initial="+letter , "word="+element.value ];

   // Build URL
   var baseurl = "checkword.php";
   var url = baseurl + "?" + params.join("&");

   var req = Ajax.getTransport();

   req.onreadystatechange=function() {
     if ((req.readyState==4)) {
       if (req.status==200) {
         handleResponse(req,element);
       } else {
         handleError(req,element);
       }
     }
   }

   // Dispatch request
   req.open("GET",url,true);
   req.send(null);
}

On the server side, the story is also pretty simple. First I check that the expected parameters are present and return an error if they aren't:

$word = $_GET['word'];
$initial = $_GET['initial'];

if (!($word && $initial)) {
    header("HTTP/1.0 400 Bad Request");
    echo "Missing parameters!";
    exit;
}

Then a function call to check_word() validates the word in the request, returning a prepared JSON response. This response is printed directly to the HTTP response and the script terminates.

echo check_word($word, $initial);
exit;

The check_word() function itself is straightforward, using the pspell library to see if a given word is in the dictionary. The script uses JSON-formatted data to respond to the request, setting a property named "success" to true or false. If a word isn't valid, an extra property named "reason" is added to the JSON response explaining why the validation failed.

Listing 6.16

function check_word($word,$initial) {

   $pspell_link = pspell_new("en");

   $word = strtolower($word);
   $initial = strtolower($initial);

   // Check word starts with correct letter
   if(substr($word,0,1) != $initial) {

     $success = false;
     $reason = "doesn't begin with '".$initial."'";

   } else if (!pspell_check($pspell_link,$word)) {

     // Word is not in dictionary
     $success = false;
     $reason = "not in dictionary";
   } else {

     $success = true;
   }

   // Create JSON-encoded result
   if ($success) {
     $result = "{ success : true }";
   } else {
     $result = "{ success : false , reason : \"".$reason."\" }";
   }

   return $result;
}

Back on the client, I need to write some code to handle the JSON response. I'm using the json.js parser here from json.org, which adds a safe parseJSON() function to all JavaScript strings. All I have to do is grab the AJAX request's responseText property and call parseJSON() on it to turn it into a JavaScript object. Then it's just a case of updating the input element's status area according to the JSON data.

Listing 6.17

handleResponse = function(req,element) {

   // Evaluate JSON response
   var response = req.responseText.parseJSON();

   // Get input's status element
   var statusId = element.id.substring(0,1) + "-status";
   var statusElement = $(statusId);

   // Update status element according to response data
   if (response.success) {
     statusElement.style.color = "green";
     statusElement.innerHTML = "OK";
   } else {
     statusElement.style.color = "red";
     statusElement.innerHTML = response.reason;
   }
}

This screenshot shows the system in action, with the FireBug console tracking the AJAX requests:

As expected, in Figure 6.14 you can see a request being made for every field that's filled in. The most recent response has been expanded so that you can see the JSON response text from the server.

Now, suppose we have a couple of thousand people who spend their workday entering data onto screens like this. The server would be swamped with requests. Although the actual validation calls may be quite cheap, there's still the cost of establishing and tearing down each individual TCP connection, and the risk of overwhelming the server's connection/process pool.

We can alleviate the situation by using request batching. Simply put, request batching means queuing up requests, and sending them all to the server in a single AJAX call. To adapt the current system to use call batching, changes are needed in the following areas:

  • Instead of input validation calls being made immediately, they must be put in a queue.
  • Periodically, the queued validation calls must be combined into a single AJAX request.
  • On the server, the calls must be unbundled, and each processed in turn.
  • The server must combine the results of the multiple validation calls in a batch for return to the client.
  • The client then has to unbundle the batched responses, and handle each one appropriately.
Starting with the first of these steps, let's turn to the client JavaScript. We have to introduce some global variables to maintain state:

var nextBatch = new Array();

var nextCorrId = 0;
var callbackMap = new Array();

nextBatch is where queued requests will be stored until they're sent. We'll look at the other variables soon. Now, we'll modify the validate() function so that instead of making an AJAX call directly to the server, it simply adds its parameters to the next batch of calls:

validate = function(element) {
   var letter = element.id.substring(0,1);

   var params = [ "initial="+letter , "word="+element.value ];
   addToBatch(element,params,handleResponse);
}

Now we can implement the addToBatch() function. It takes three parameters: the element that's being validated, the validation request parameter array, and the callback function to invoke the response to this request:

Listing 6.18

addToBatch = function(element,params,responseHandler) {

   var corrId = getCorrId();
   callbackMap[corrId] = {
           "callback" : responseHandler,
           "element" : element
       };

   params[params.length]= "corrId="+corrId;

   var query = params.join("&");

   nextBatch[nextBatch.length] = query;

   if (nextBatch.length==1) {
     window.setTimeout(dispatchBatchedRequests,3000);
   }
}

When sending a batch of requests together, and getting a batched response, it's important to be able to figure out which response belongs to each request. This is why a unique correlation ID is added to the request parameters - the same ID will be used in the server's response to this query. We'll be able to use the correlation ID to look in the callbackMap array and find the correct element and response handler to associate with the response.

After adding this request to the next batch, the size of the batch is checked. If the current query is the first one to be added to the batch, a timeout is initiated to dispatch the batched requests. Any subsequent validate() calls within the next three seconds make it into this batch, and then a new batch and countdown will be initiated by the next validate() call.

When the timeout completes, it's time to send whatever batched requests we have to the server. Each AJAX request will have a single parameter that tells the server how many queries are in the batch, followed by a numbered sequence of special parameters, each of which represents a single batched request.

dispatchBatchedRequests = function() {

   // Copy the contents of the waiting request batch,
   // then clear it
   var batch = nextBatch.concat();
   nextBatch = new Array();

   // Tell the server how many batched requests to expect
   var params = ["num="+batch.length];

   // Make each batched request into a query parameter
   for (var i=0; i<batch.length; i++) {
     params[params.length] = "req"+i+"="+escape(batch[i]);
   }

   // Send request to server
   var baseurl = "checkword-batched.php";
   var url=baseurl+"?"+params.join("&");

   var req = Ajax.getTransport();

   req.onreadystatechange=function() {
     if ((req.readyState==4)) {
       if (req.status==200) {
         handleBatchResponse(req);
       } else {
         handleError(req);
       }
     }
   }

   req.open("GET",url,true);
   req.send(null);
}

Simply put, this function loops over the batched requests, grabs their request parameters, and turns them into a parameter of a master request. Since each querystring created by addToBatch() is now itself a parameter of another querystring, it's necessary to use escape() to encode its special characters. When the AJAX call completes, a function called handleBatchResponse() will be invoked to deal with the batched response.

Now let's turn to the server side. The first thing to do is check how many requests are in the batch.

$num_requests = $_GET['num'];

if (!$num_requests) {
     bad_request("num param not specified");
}

Since there are several more ways for the request to be invalid now, we've extracted the HTTP error-generation code to its own function:

function bad_request($reason) {
     header("HTTP/1.0 400 Bad Request");
     echo $reason;
     exit;
}

Having established how many requests to expect, the script now loops over the special variables named req0, req1, etc., each of which represents an encoded querystring. The querystring is decoded and then split into a parameter array with a call to PHP's built-in parse_str() function. After checking that the expected parameters are present, the decoded request is added to an array for processing later:

Listing 6.19

// Loop over batched requests
   $requests = Array();
   for ($i = 0 ; $i < $num_requests ; $i++) {

     // Get query parameters for sub-request
     if (!$_GET['req'.$i]) {
       bad_request("req".$i." missing");
     }
     $querystring = urldecode($_GET['req'.$i]);
     $params = Array();
     parse_str($querystring, &$params);

     // Check sub-request parameters
     if (!($params["word"] &&
         $params["initial"] &&
         $params["corrId"])
       ) {
       bad_request("missing params for req".$i);
     }

     // Store decoded parameters
     $requests[$i] = $params;
   }

Now it's simple to loop over each set of query parameters and call the check_word() function on each one. check_word() returns a JSON result for each word it validates, but we now need to associate each of these results with the request that it belongs to. This is where the correlation ID comes in:

Listing 6.20

$responses = Array();

   for ($i = 0 ; $i < $num_requests ; $i++) {
     $word = $requests[$i]["word"];
     $initial = $requests[$i]["initial"];
     $corrId = $requests[$i]["corrId"];

     $responses[$i] = $corrId." : ". check_word($word,$initial);
   }

   // Batch and return responses
   echo "{";
   echo implode($responses,", \n");
   echo "\n}";

Simply put, this code builds a JSON array that maps each correlation ID to the JSON representation of its validation result. A complete response for a batch of two requests might look like this:

{ req0 : { success : true },
req1 : {success : false, reason: "not in dictionary"}
}

Let's head back to the client and see how this batched response is dealt with:

Listing 6.21

handleBatchResponse = function(req) {

   // Evaluate JSON response
   var batchresponse = req.responseText.parseJSON();

   // Loop over each response
   for (var corrId in batchresponse) {

     var response = batchresponse[corrId];

     // Look up target input, and invoke callback.
     var element = callbackMap[corrId].element;
     var callbackFunction = callbackMap[corrId].callback;
     callbackFunction(response,element);
   }
}

What's happening here? First, we evaluate the JSON response as we've seen before. Then, we loop through the correlation IDs in the JSON response. For each one, we extract the corresponding validation result from the JSON, and then retrieve the input element and callback function that were stored in the callbackMap, back when addToBatch() was called. Finally, the callback function is invoked.

In this example, the callback function is always handleResponse, which hasn't changed much since we saw it earlier:

Listing 6.22

handleResponse = function(response,element) {

   var statusId = element.id.substring(0,1) + "-status";
   var statusElement = $(statusId);
   if (response.success) {
     statusElement.style.color = "green";
     statusElement.innerHTML = "OK";
   } else {
     statusElement.style.color = "red";
     statusElement.innerHTML = response.reason;
   }
}

This time, instead of a request being made for every input, the validation calls have been combined into two batches. The GET URLs in the Firebug console show that the first batch contained six requests and the second contained three. The response of the second request has been expanded, showing the batched JSON response.

The disadvantage in batching calls this way is the delay that's introduced between the user updating a field and the value being sent for validation. However, in a high-load production environment, the reduction in concurrent requests will lead to a performance boost that may offset the delay. Of course, the batching timeout can be fine-tuned for your own application, or even varied depending on server load, as we'll see later.

In this scenario, all of the batched requests we made were to the same URL. In a real-world application, your AJAX client may talk to different server resources during its operation. There are a couple of approaches that can be taken here: the simplest is to maintain a separate request queue for each of the server resources that your client communicates with and batch requests to each service separately.

Alternatively, each request in the batch could have an extra parameter representing the server resource it's intended for. The batched AJAX call would always be made to a special dispatcher service, which would unbatch each request and route it to the desired resource on the server before collating and returning the batched results. This is similar to the Front Controller pattern popularized by the Java Struts framework.

On the client side, the example code already has the capability of calling different JavaScript handler functions for each response via the callback map. Combined with a server-side request router, this allows for complete heterogeneity and independence of requests in a batch.

Request Throttling
In a distributed client/server environment like an AJAX application, there's always a risk of the server being overloaded by traffic from the clients. Especially in the case of public Web applications, surges in uptake and usage can be sudden and unexpected. Well-thought-out AJAX systems should incorporate contingency measures for such traffic spikes, allowing the application to degrade gracefully instead of grinding to a halt.

For an AJAX application, degrading gracefully under load is a two-pronged strategy. First, you need a mechanism to throttle client calls during busy times, limiting the number of requests that the server has to deal with. Second, AJAX clients need a coping strategy when the server is tied up and temporarily fails to respond to requests.

In this section, we're going to introduce a very simple sports-score ticker application that polls the server for updates. This is a commonly employed pattern in AJAX clients and is used when a client needs to get updates independent of user input. Other examples include chat services (where the client needs to poll for messages from other users), e-mail applications (where the client polls to check for new mail), and other tickers for stock prices or news headlines.

Our soccer ticker is implemented by a simple PHP script on the server side, which simulates a game by randomly picking events from a table. Each event has a message string, and some events represent a goal being scored, upping one team's score. When a request is handled, the script randomly decides whether an event has occurred. If it has, a JSON message is returned with the message string and the game's score. Otherwise only the score is returned.

The client-side JavaScript simply polls the game-simulator script every few seconds and updates the GUI with the JSON update returned. Also, because the PHP script is dumb and stateless, the client has to keep track of the score and include it in each poll request. Figure 6.16 demonstrates the ticker in action:

The FireBug console shows some of the AJAX polls. The most recent server response is also shown, with the JSON used to generate the current display.

Now, let's suppose that the default poll interval is set to 10 seconds. This is done with a setTimeout() call in the response handler function.

handleResponse = function(req) {

     // update UI
     // ...

     // ...

     // Poll again in ten seconds      window.setTimeout(getUpdate, 10000); }

Let's also suppose that this is an important game, and a few hundred thousand soccer fans decide to keep track of it with the score ticker. After a while the server starts to creak. We need the clients to back off a little and poll less frequently. Throttling strategies can be very sophisticated or very simple. For purposes of this example, I'm going to use a throttling strategy that can be stated as:

"If the server is busy, clients should poll every 60 seconds. Otherwise clients should poll every 10 seconds."

This strategy can be implemented either in the client code or on the server. Let's look at a way to do it on the client first.

When dispatching a poll request, we can get the current system time and associate it with the AJAX request in a wrapper object:

var req = Ajax.getTransport();

var reqWrapper = {
           "req" : req,
           "dispatchTime" : new Date()
       };

Ideally, we'd simply be able to add the dispatch time to the request directly as a property. Unfortunately, Internet Explorer's implementation of XMLHttpRequest doesn't allow arbitrary properties to be set, so this wrapper approach is a necessary workaround.

The handleResponse function is altered to expect its request to be wrapped like this. Then, when the handler is called, this dispatchTime property can be compared to the current time, and we can use the difference to decide whether the server is busy.

Listing 6.23

handleResponse = function(reqWrapper) {

     var req = reqWrapper.req;
     var dispatchTime = reqWrapper.dispatchTime;

     var currentTime = new Date();
     var deltaMillis = currentTime - dispatchTime;

     var pollInterval;

     // If server took > 10 seconds to respond,
     // it must be busy
     if (deltaMillis > 10000) {
       pollInterval = 60000;
     } else {
       pollInterval = 10000;
     }

     // update UI
     // ...
     // ...

     // Poll again after interval has passed
     window.setTimeout(getUpdate, pollInterval);
}

This approach is simple and effective, but slightly crude. We're assuming that the server is busy if it takes a long time for the request to complete, but this could be caused by other factors. For instance, the ISP's router may be congested, or we may be using up all of the local bandwidth with Torrent downloads. Maybe we're using a 9600-baud modem. OK, probably not that last one, but the point is that the client can only measure request round-trip times, and guessing at server load from that information is error-prone. Now let's see how we can control request-throttling from the server. Depending on your server-side environment, you may be able to measure load in some sophisticated ways. You may be able to count the number of clients that are currently connected, and use that as a metric of "busyness." You could keep track of the number of requests coming in per minute. Maybe you even have a cluster of servers with a sophisticated management layer that can be queried to ascertain the overall system load.

Let's keep things simple, though, and just assume that we have a Boolean variable named "busy" that's been populated with "true" or "false" depending on some metric. Now, when the ticker update script is ready to return its response, it can add an extra property to the JSON response telling the client how long to wait before polling again:

Listing 6.24

// Generate response for ticker
print("{\n");
printf("score : [%d,%d]",$score[0],$score[1]);

if ($message) {
   print(",\n");
   printf("message : '%s',\n",$message);
}

// Determine time to next poll
if ($busy) {
     $pollinterval = 60000;
} else {
     $pollinterval = 10000;
}
// Add pollinterval to response
print("pollInterval : %d", $pollinterval);

print("\n}");
exit;

So now the server is making the decision about how long the client should wait before polling again, rather than the client deciding for itself. Back in the client-side response handler:

handleResponse = function(req) {

     var response = req.responseText.parseJSON();

     // update UI
     // ...
     // ...

     window.setTimeout(getUpdate,response.pollInterval);
}

Having the server send back its status with each AJAX request can be a useful technique even when you don't have a client that relies on continuous polling. It could be used to instruct the client to operate temporarily in a less chatty way, perhaps switching to batched requests as we saw earlier, or even by disabling some of the client's less important AJAX functionality.

We've now explored several techniques for lightening the load that AJAX clients can put on their servers. However, you should always plan for the worst - a server that fails to respond at all. In the next section we'll look at ways to cope with this scenario.

Guarding Against Hung Services
While browsing the Web, we've all come across the situation where the site fails to respond. Either the browser's progress indicator spins away indefinitely, or an intervening proxy server responds with an error. So what if this happens to an AJAX request? Let's see how to implement a timeout condition for AJAX requests.

In the case of a proxy server sitting between the AJAX client and server, things are easy. When a proxy server detects that the upstream server it's connecting to has taken too long to respond, it returns a 504 gateway timeout response to the client. It's trivial to add this check to the usual readystatechange functionality:

Listing 6.25

req.onreadystatechange=function() {

   if ((req.readyState==4)) {

     if (req.status==200) {

       handleResponse(req);
     } else if (req.status==504) {

       // Proxy server has detected a timeout
       handleTimeout(req);
     } else {

       handleError(req);
       }
     }
   }

   req.open("GET",url,true);
   req.send(null);

When there's no proxy server to intervene, the situation is quite a bit more complicated. The basic plan of attack is as follows:

  • Use window.setTimeout to start a timer when the AJAX request is dispatched.
  • If the timeout expires before getting a response, abort the request and call a handler function.
  • If a response is received before the timeout expires, clear the timeout.
First, we want to start a timeout before the call to req.send(). setTimeout takes a callback function as an argument, which is invoked when the timeout expires. We can use an anonymous function to call abort() on the local AJAX request reference. Let's see what we have so far:

// Freak out if server takes more than
// 10 seconds to respond.

   window.setTimeout(
       function() {
         req.abort();
         },
       10000);

   req.open("GET",url,true);
   req.send(null);

When we test this against the server-side script that we set up to block for 30 seconds, something strange happens. Immediately after we call req.abort(), the onreadystatechange handler fires. In Internet Explorer, execution drops through to the handleError call, while Firefox throws an exception when req.status is accessed. Maybe it's possible to remove the onreadystatechange handler before calling abort?

window.setTimeout(
     function() {
         delete req.onreadystatechange; // doesn't work!
         req.abort();
         },
     1000);

Unfortunately, this doesn't solve the problem on either browser - onreadystatechange is still invoked after the abort() call. The answer is simply to use a local variable to keep track of whether the request has been aborted:

var aborted = false;
window.setTimeout(
     function() {
       aborted = true;
       req.abort();
       },
     1000);

Because the anonymous timeout function and the onreadystatechange handler function are both declared in the same scope block, they both have access to the local "aborted" variable. The onreadystatechange handler just has to make sure this variable is false, and exit if it isn't.

req.onreadystatechange=function() {
     if (aborted) {
       return;
     }
     // ...
}

This works correctly, preventing any weird behavior after the call to abort(). Now we can add a callback to handle timeouts to my anonymous function:

var aborted = false;
window.setTimeout(
     function() {
       aborted = true;
     req.abort();

       // Invoke timeout handler
       handleTimeout(null);
       },
     1000);

The properties of an aborted AJAX request are garbage, and attempting to access them in Firefox can cause exceptions. It's safer just to pass null to the handleTimeout() function in this case.

Now there's one thing left to do. We have to prevent the abort timeout from firing if a response is received from the server. To do this, we need another local variable to record the reference to the pending timeout, so it can be cancelled later.

     var aborted = false;

     var abortTimer;

     // Setup abort timeout and keep a local reference
     // to it.
     abortTimer = window.setTimeout(
       function() {
         aborted = true;
         req.abort();
         handleTimeout(null,element);
         },
       1000);

     req.open("GET",url,true);
     req.onreadystatechange=function() {
       if (aborted) {
         return;
       }

     // If we've reached state 3 ("interactive"),
     // the we have a server response.
     if(req.readyState >= 3) {

       // Prevent abort timeout from firing.
       window.clearTimeout(abortTimer);

     }

     // ...
}

That's all there is to it. Implementing the handleTimeout() function really depends on the nature of the application and the nature of the request. If the request was simply a periodic poll for updates, as in the soccer score ticker, it may be okay to swallow the error silently and keep trying, only notifying the user if communication with the server hasn't been reestablished after a given number of tries. On the other hand, if the timed-out request was generated by an explicit and important user action such as hitting "Save" on a form or "Send" on a e-mail, the user should be told right away that there's a problem, so he doesn't incorrectly assume that the action has been completed. If you really wanted to be sophisticated, you might put failed requests in a queue, and then batch-resubmit them when the server is available again.

Summary
AJAX is all about breaking out of the Request-Response cycle just by retrieving the data that's needed when it's driving a more interactive presentation in the browser. Web services come first to the mind of many developers as obvious candidates to provide that data, since they're designed to provide structured data in response to queries made over HTTP. Web services provide a powerful mechanism not only for query and data retrieval, but also for interacting with remote entities and modifying their state. In this chapter we reviewed a broad range of Web services, which we understand encompass any kind of programmatic interface that can be called over HTTP.

At its simplest this definition of Web services encompasses services based on ad hoc HTTP queries returning simple values as plain text through more structured services that use XML or JSON to respond with structured data to the semantics of REST and standardized response document formats such as RSS. AJAX can also call on more formalized web services by implementing protocols on top of simple HTTP such as XML-RPC and even SOAP, although these standards are arguably overkill for a browser based client.

One drawback to using AJAX to access remote Web services is the cross-domain restriction put on the XMLHttpRequest object. While this restriction can be viewed as an important browser security measure, it creates extra work for AJAX developers. A simple solution is to use a cross-domain proxy to pass requests from a Web page's origin server to the host of the Web service you want to call. Most Web servers support proxying through simple configuration (for instance Apache has mod_rewrite), or proxying can be done programmatically with a few lines of code in most Web programming environments. AJAX programming can involve a lot of "boilerplate" code on the client to marshal and unmarshal data packets, send requests and handle responses, and deal with errors. The same applies on the server side, where data structures have to be serialized as XML or JSON strings.

However, as AJAX matures, more and more help is at hand in the form of frameworks, toolkits, and libraries that do the heavy lifting for you. In this chapter we've shown how the Prototype library can simplify your AJAX code by wrapping the XMLHttpRequest with its own AJAX object. We also saw how TinyAjax can be used in a PHP environment not only to take care of crossdomain proxying, but to auto-generate JavaScript to simplify the client-side code you have to write. There are many other server-side AJAX frameworks, and it's worth researching what's available for your Web programming environment of choice - it could save you from a lot of laborious coding.

In the last section we considered the load that AJAX clients can put on a server and some of the problems that can arise. It's a good idea to do some kind of load testing on your AJAX service during development to see how it will cope when dealing with multiple concurrent clients. If you run into performance issues, some of the ways we looked at to lessen server load are batching multiple requests together and throttling client requests when the server becomes busy. These strategies ultimately boil down to making your client less "chatty," talking to the server only when necessary and queuing data on the client in the meantime.

We've also looked at implementing an AJAX request timeout mechanism and discussed what to do when timeouts occur.

This content is reprinted from Real-World AJAX: Secrets of the Masters published by SYS-CON Books. To order the entire book now along with companion DVDs, click here to order.

More Stories By Corey Gilmore

Corey Gilmore is the president of CFG Consulting, Inc., specializing in developing rich internet applications with ColdFusion, PHP and Ajax for the Federal government and Fortune 100 clients. He guiltily enjoys designing and implementing low-cost, high performance business continuity plans using VMware ESX server. As the former Director of Information Technology for the United States Senate Democratic Leadership, he designed and implemented a continuity of operations plan to ensure Senate business continuity in the event of a disaster. Corey can be reached at cfgci.com.

More Stories By Jason Blum

Jason Blum is principal engineer with the advanced technologies development team in the United States Senate, Office of the Sergeant at Arms. Formerly the lead administrator of the Senate’s shared Web hosting environment, Jason now designs and manages the implementation of schema and pattern-centric solutions for Senate offices in XML, ColdFusion, Flex, and .NET. He is a Certified Advanced ColdFusion developer with a BA in philosophy, Masters Degrees in philosophy of education and in IT, and an intermediate certification in Hungarian from itk.hu.

More Stories By Phil McCarthy

Philip McCarthy is a UK-based software development consultant
specializing in J2EE and Web technologies. An early adopter of rich
browser-based client development, he has several years' experience of integrating Ajax technologies into enterprise Java frameworks, gained on projects in the financial services, telecoms, and digital media sectors. Philip is also the author of the "Ajax for Java Developers" series for IBM developerWorks, and blogs about software development at chimpen.com.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.