AJAX Tutorial: Asynchronous Server Calls and XMLHttpRequest

Hello, server!

XMLHttpRequest Example Explanations

This is a JavaScript DOM exercise from Chapter 2 of Microsoft AJAX Library Essentials: JavaScript in ASP.NET AJAX 1.0 Explained.

XMLHttpRequest is the object that enables the JavaScript code to make asynchronous HTTP server requests. This allows you to initiate HTTP requests and receive responses from the server in the background, without requiring the user to submit the page to the server. This feature, combined with the possibility to manipulate the web page using DOM and CSS, allows you to implement responsive functionality and visual effects backed with live data from the server, without the user experiencing any visual interruptions. This is AJAX.

The XMLHttpRequest object was initially implemented by Microsoft in 1999 as an ActiveX object in Internet Explorer, and eventually became de facto standard for all the browsers, being supported as a native object by all modern web browsers except Internet Explorer 6.

Note that even if XMLHttpRequest has become a de facto standard in the web browsers, it is not yet a W3C standard. Similar functionality is proposed by the W3C DOM Level 3 Load and Save specification standard, which hasn't been implemented yet by web browsers.

The typical sequence of operations when working with XMLHttpRequest is as follows:

  1. Create an instance of the XMLHttpRequest object.
  2. Use the XMLHttpRequest object to make an asynchronous call to a server page, defining a callback function that will be executed automatically when the server response is received.
  3. Read the server's response in the callback function.
  4. Update the web page using the data received from the server.
  5. Go to step 2.
Let's now see how to do these steps with real code.

Creating the XMLHttpRequest Object

The XMLHttpRequest is implemented in different ways by the browsers. In Internet Explorer 6 and older, XMLHttpRequest is implemented as an ActiveX control, and you instantiate it like this:

xmlhttp = new ActiveXObject("Microsoft.XMLHttp"); 

For the other web browsers – including Firefox, Opera and Safari, and Internet Explorer 7, XMLHttpRequest is a native object, so you create instances of it like this:

xmlhttp = new XMLHttpRequest(); 

The ActiveX XMLHttp library comes is many more flavors and versions that you could imagine. Each piece of Microsoft software, including Internet Explorer and MDAC, came with new versions of this ActiveX control, each having its own name. Microsoft.XMLHttp is the oldest and is supported on all Windows machines, but the newer versions have performance improvements.

It is possible to write JavaScript code that automatically detects the latest XMLHttp version installed on the visitor’s machine, if he or she is using Internet Explorer 6 or older. The technique is described in AJAX and PHP: Building Responsive Web Applications (Packt, 2006), but we will not insist on it here because the feature is included in the Microsoft AJAX Library.

The following JavaScript function creates an XMLHttpRequest instance by using the native object if available, or the Microsoft.XMLHttp ActiveX control for visitors that use Internet Explorer 6 or older:

// creates an XMLHttpRequest instance
function createXMLHttpRequestObject()
{
  // xmlHttp will store the reference to the XMLHttpRequest object
  var xmlHttp;
  // try to instantiate the native XMLHttpRequest object
  try
  {
    // create an XMLHttpRequest object
    xmlHttp = new XMLHttpRequest();
  }
  catch(e)
  {
    // assume IE6 or older
    try
    {
      xmlHttp = new ActiveXObject("Microsoft.XMLHttp");
    }
    catch(e) { }
  }
  // return the created object or display an error message
  if (!xmlHttp)
    alert("Error creating the XMLHttpRequest object.");
  else 
    return xmlHttp;
}

This function uses the JavaScript try/catch construct, which is a powerful exception-handling technique that was initially implemented in OOP (Object Oriented Programming) languages. Basically, when an error happens at run time in the JavaScript code, an exception is thrown. The exception is an object that contains the details of the error. Using the try/catch syntax, you can catch the exception and handle it locally, so that the error won't be propagated to the user's browser.

The try/catch syntax is as follows:

try
{
  // code that might generate an exception
}
catch (e)
{
  // code that executes if an exception was thrown in the try block
  // (exception details are available through the e parameter)
}

You place any code that might generate errors inside the try block. If an error happens, the execution is passed immediately to the catch block. If no error happens inside the try block, then the code in the catch block never executes.

Run-time exceptions propagate from the point they were raised, up through the call stack of your program. The call stack is the list of methods that are being executed. So if a function A() calls a function B() which at its turn calls a function called C(), then the call stack will be formed of these three methods. If an exception happens in C(), you can handle it using a try/catch block right there. If the exception isn't caught and handled in C(), it propagates, to B(), and so on. The final layer is the web browser. If your code generates an exception that you don't handle, the exception will end up getting caught by the web browser, which may display an unpleasant error message to your visitor.

The way you handle each exception depends very much on the situation at hand. Sometimes you will simply ignore the error, other times you will flag it somehow in the code, or you will display an error message to your visitor. In this book you will meet all kinds of scenarios.

In our particular case, when we want to create an XMLHttpRequest object, we will first try to create the object as if it was a native browser object, like this:

  
  // try to instantiate the native XMLHttpRequest object
  try
  {
    // create an XMLHttpRequest object
    xmlHttp = new XMLHttpRequest();
  }

Internet Explorer 7, Mozilla, Opera, Safari, and other browsers will execute this piece of code just fine, and no error will be generated, because XMLHttpRequest is a supported natively. However, Internet Explorer 6 and its older versions won't recognize the XMLHttpRequest object, an exception will be generated, and the execution will be passed to the catch block. For Internet Explorer 6 and older versions, the XMLHttpRequest object needs to be created as an ActiveX control:

  
  catch(e)
  {
    // assume IE6 or older
    try
    {
      xmlHttp = new ActiveXObject("Microsoft.XMLHttp");
    }
    catch(e) { }
  }

Every JavaScript programmer seems to have his or her own technique for creating the XMLHttpRequest object, and surprisingly enough, all techniques work just fine. The implementation we presented uses try and catch to instantiate the object, because it (reasonably) guarantees the best chance of working well with future browsers, while doing proper error checking without consuming too many lines of code.

Alternatively, you could, for example, check whether your browser supports XMLHttpRequest before trying to instantiate it, using the typeof function:

if (typeof XMLHttpRequest != "undefined")
{
  xmlHttp = new XMLHttpRequest();
}

Using typeof can often prove to be very helpful. In our particular case, using typeof doesn't eliminate the need to guard against errors using try/catch, so you would just end up typing more lines of code.

Another technique is to use a JavaScript feature called object detection. This feature allows you to check whether a particular object is supported by the browser, and works like this:

if (window.XMLHttpRequest)
{
  xmlHttp = new XMLHttpRequest();
}

If window.ActiveX exists, then you know that the browser is Internet Explorer. Once again, we're not using this technique because it would simply add more lines of code without bringing any benefits; but the ideas are good to keep nevertheless.

If you decide to use object detection, please be sure to check for XMLHttpRequest first before checking for ActiveX support. The reason for this recommendation is Internet Explorer 7, which supports both the ActiveX XMLHttp control, and the native XMLHttpRequest object (this one being the better choice).

At the end of our createXMLHttpRequestObject function, we test that after all the efforts, we have ended up obtaining a valid XMLHttpRequest instance:

  // return the created object or display an error message
  if (!xmlHttp)
    alert("Error creating the XMLHttpRequest object.");
  else
    return xmlHttp;

Here we used the reverse effect of JavaScript’s object detection feature, which in our opinion is even nicer than the feature itself. Object detection says that JavaScript will evaluate a valid object instance, such as xmlHttp, to true. The negation of this expression, (!xmlHttp), returns true not only if xmlHttp is false, but also if it is null or undefined.

Initiating Server Requests

After creating the XMLHttpRequest object you can do lots of interesting things with it. Although it has different ways of being instantiated, all the instances of XMLHttpRequest are supposed to share the same API (Application Programming Interface) and support the same functionality. This API is formed of the following methods and properties:

Method/Property

Description

abort

Stops the current request.

getAllResponseHeaders()

Returns the response headers as a string.

getResponseHeader("headerLabel")

Returns a single response header as a string.

open("method", "URL"[, asyncFlag[, "userName"[, "password"]]])

Initializes the request parameters.

send(content)

Performs the HTTP request.

setRequestHeader ("label", "value")

Sets a HTTP request header.

onreadystatechange

Used to set the callback function that handles request state changes.

readyState

Returns the status of the request:

0 = uninitialized
1 = loading
2 = loaded
3 = interactive
4 = complete

responseText

Returns the server response as a string.

responseXML

Returns the server response as an XML document that can be manipulated using JavaScript's DOM functions.

status

Returns the status code of the request.

statusText

Returns the status message of the request.

The methods you will use with every server request are open() and send(). The open() method configures a request by setting various parameters, and send() sends the request to the server. When the request is made asynchronously, before calling send you will also need to set the onreadystatechange property with the callback method to be executed when the status of the request changes, thus enabling the AJAX mechanism.

The open() method is used for initializing a request. It has two required parameters and a few optional ones. The open() method doesn't initiate a connection to the server; it is only used to set the connection options. The first parameter specifies the method used to send data to the server page, and it can have a value such as GET, POST, or PUT. The second parameter is URL, which specifies where you want to send the request. The URL can be complete or relative. If the URL doesn't specify a resource accessible via HTTP, the first parameter is ignored.

The third parameter of open(), called async, specifies whether the request should be handled asynchronously; true means that your code processing carries on after the send() method returns without waiting for a response from the server; false means that the script waits for a response before continuing processing, freezing the web page functionality. To enable asynchronous processing (which is the heart of the AJAX mechanism), you will need to set async to true, and handle the onreadystatechange event to process the response from the server.

When using GET to pass parameters, you send the parameters using the URL's query string, as in http://localhost/ajax/test.aspx?param1=x¶m2=y. This server request passes two parameters to the server—a parameter called param1 with the value x, and a parameter called param2 with the value y:

// call the server to execute the server side operation
xmlHttp.open("GET", "http://localhost/ajax/test.aspx?param1=x¶m2=y", true);
xmlHttp.onreadystatechange = handleRequestStateChange;
xmlHttp.send(null);

When using POST, you send the query string as a parameter of the send() method, instead of joining it on to the base URL, like this:

// call the server page to execute the server side operation
xmlHttp.open("POST", "http://localhost/ajax/test.aspx", true);
xmlHttp.onreadystatechange = handleRequestStateChange;
xmlHttp.send("param1=x¶m2=y");

The to code samples should have the same effects. In practice, there are a few differences between POST and GET that you should know about:

The minimal implementation of a function named process() that makes asynchronous server calls using GET looks like this:

function process()
{
  // call the server to execute the server side operation
  xmlHttp.open("GET", "server_script.php", true);
  xmlHttp.onreadystatechange = handleRequestStateChange;
  xmlHttp.send(null);
}

This method has the following potential problems:

A better version of process() looks like that:

// performs a server request and assigns a callback function
function process()
{
  // continue only if xmlHttp isn't void
  if (xmlHttp)
  {
    // try to connect to the server
    try
    {
      // initiate server request
      xmlHttp.open("GET", "server_script.php", true);
      xmlHttp.onreadystatechange = handleRequestStateChange;
      xmlHttp.send(null);
    }
    // display an error in case of failure
    catch (e)
    {
      alert("Can't connect to server:\n" + e.toString());
    }
  }
}

If xmlHttp is null (or false) we don't display yet another error message, because we assume such a message was already displayed by the createXMLHttpRequestObject function. We make sure to signal any other connection problems though.

Handling the XMLHttpRequest Server Response

When making an asynchronous request (such as in the code snippets presented earlier), the execution of xmlHttp.send() doesn't freeze until the server response is received; instead, the execution continues normally. In the process() function shown earlier, the handleRequestStateChange() function is defined as the callback method that should handle request state changes.

Usually, handleRequestStateChange() is called four times, for each time the request enters a new stage. The readyState property can have one the following values representing the possible stages of the request:

    0 = uninitialized
    1 = loading
    2 = loaded
    3 = interactive
    4 = complete

Except state 3, all the other states have pretty self-explaining names. The interactive state is an intermediate state when the response has been partially received. In our AJAX applications we will only use the complete state, which marks that a response has been fully received from the server.

The typical implementation of handleRequestStateChange is shown in the following code snippet, which highlights the portion where you actually get to read the response from the server.

// function executed when the state of the request changes
function handleRequestStateChange() 
{
  // continue if the process is completed
  if (xmlHttp.readyState == 4) 
  {
    // continue only if HTTP status is "OK"
    if (xmlHttp.status == 200) 
    {
      // retrieve the response
      response = xmlHttp.responseText;

      // do something with the response
      // ...
      // ...
    }
  }
}

Before attempting to read the received data, we also verify that the response status code is 200. Sending such a code indicating the status of the request is part of the HTTP protocol, and 200 is the status code that specifies that the request completed successfully. Other popular HTTP status codes are 404, which indicate that the requested resource couldn’t be found, and 500, which indicates a server error.

Once again we can use try/catch blocks to handle errors that could happen while initiating a connection to the server, or while reading the response from the server. A safer version of the handleRequestStateChange() function looks like this:

// function executed when the state of the request changes
function handleRequestStateChange() 
{
  // continue if the process is completed
  if (xmlHttp.readyState == 4) 
  {
    // continue only if HTTP status is "OK"
    if (xmlHttp.status == 200) 
    {
      try
      {
        // retrieve the response
        response = xmlHttp.responseText;
        // do something with the response
        // ...
        // ...
      }
      catch(e)
      {
        // display error message
        alert("Error reading the response: " + e.toString());
      }
    } 
    else
    {
      // display status message
      alert("There was a problem retrieving the data:\n" + 
            xmlHttp.statusText);
    }
  }
}

Implement the exercise step by step and find detailed explanations in our book, Microsoft AJAX Library Essentials: JavaScript in ASP.NET AJAX 1.0 Explained.