Asynchronous Request Objects



The term Ajax was first mentioned in the article Ajax: A New Approach to Web Applications by Jesse James Garrett. This article talked that Google Suggest and Google Maps used the approach have been calling Ajax in Adaptive Path.

"Google Suggest and Google Maps are two examples of a new approach to web applications that we at Adaptive Path have been calling Ajax. The name is shorthand for Asynchronous JavaScript + XML, and it represents a fundamental shift in what's possible on the Web." 

This quotation says that, Ajax is a concept for Asynchronous JavaScript + XML. XML is used for exchanging structured data between applications, but not the only one usable format.

The core concept of Ajax is asynchronous, and why asynchronous? Browsers have their default action for the traditional form submitting or hyperlink clicking. That is transferring a request synchronously, waiting for the response from a server, and refreshing the page. What users can do is to wait for the latest response displayed in a new page. If they perform a new action before the response is completed, the browser may abort the previous request. After the response is completed, users are shown a new page; even the updated area is only a small part of the all page.

If the request and response can be asynchronous, that is, after making the request, the browser doesn’t have to wait for a response from a server. The user can perform other action in the same page, yet doesn't interrupt the original request. After the server complete the request and send back a response, the browser will call the corresponding function to process the response, such as using DOM API to update one part of the page. If all these are possible, various interactive models will be opened.

Browsers use XMLHttpRequest to create asynchronous objects, but it has browser-compatibility problems. Internet Explorer 6 and its previous versions use ActiveXObject to implement asynchronous objects. XMLHttpRequest is supported from Internet Explorer 7. Even so, the ActiveXObject implementations are different among different versions of Internet Explorer. The basic implementation is Microsoft.XMLHTTP. The newer implementations are Msxml2.XMLHTTP, Msxml2.XMLHTTP.3.0, Msxml2.XMLHTTP.4.0, etc. Microsoft.XMLHTTP has better compatibility, so most libraries simply try to use Microsoft.XMLHTTP and Msxml2.XMLHTTP, like Prototype, or only try to use Microsoft.XMLHTTP, such as jQuery.

Creating an asynchronous object in a cross-browser way can be written as follows:
var xhr = function() {
    if(window.XMLHttpRequest) {
        return new XMLHttpRequest();
    }
    else {
        try {
            return new ActiveXObject('Microsoft.XMLHTTP');
        }
        catch(e) {
            throw new Error('XMLHttpRequest not supported');
        }
    }
};

The xhr function detects whether the XMLHttpRequest property exists. Create the instance if that's true, or try to create an ActiveXObject instance. Throw an error if it fails.

Also note that, the XMLHttpRequest implementation of Internet Explorer 7 can't request local files even the page comes from a local drive. If you really want to do so, use ActiveXObject instead.

If you need to create asynchronous objects frequently, and won't be burdened with the frequent detection of XMLHttpRequest, you can do as follows:
var xhr = window.XMLHttpRequest &&
            (window.location.protocol !== 'file:' || !window.ActiveXObject) ?
          function() {
                return new XMLHttpRequest();
          } :
          function() {
             try {
                 return new ActiveXObject('Microsoft.XMLHTTP');
             } catch(e) {
                 throw new Error('XMLHttpRequest not supported');
             }
          };

In the above example, the xhr function will create a XMLHttpRequest instance if the XMLHttpRequest property exists and the page doesn't come from a local drive; if the XMLHttpRequest property exists and the page comes from a local drive (this condition is considered for Internet Explorer 7), the xhr function will create an ActiveXObject instance.

You can create an asynchronous object as follow:
var request = xhr();

Creating an asynchronous object has browser-compatibility problems. Fortunately, the object's API has fewer differences. Some basic functions are as follows:
  • void open(string method, string url[, boolean asynch, string username, string password])
Open a connection to the server. The method parameter is the request type, like 'GET', 'POST', 'HEAD', etc. The url parameter is the server's url; if the request type is 'GET', request parameters and values can be appended to the url. The asynch parameter is a flag pointing out whether the request is asynchronous or not; if it's true, the request will be asynchronous. The username and password parameters are optional; depend on the server's requirement.

  • void setRequestHeader(string header, string value)
Set a header and value for the HTTP request. It should be called before the open function and usually used when the  method parameter is 'POST'.

  • void send(string content)
Send a request to the server. If the method parameter of open is 'GET', its content parameter should be null. If the request type is 'POST', the content parameter may be a string, XML, or JSON. They will be sent in the POST body.

  • void abort()
Abort the request.

  • string getAllResponseHeaders()
Return a string containing all HTTP response headers.

  • string getResponseHeader(string header)
Return a string containing the specified response header.

Usually, the default value true of the open function's third parameter is used. Sometimes, it may be set a false when we want to send a request synchronously. If you want to know the state of the request object, you can set the onreadystatechange property a callback function before calling the open function. Once the state is changed, the callback function is called. An example is as follow:
var request = xhr();
request.onreadystatechange = function() {
    if(request.readyState === 4) {
        if(request.state === 200) {
            // do some processing when HTTP 200 OK
        }
    }
};
request.open('GET', 'data.txt');
request.send(null);

The readyState property of the request object has four possible values representing four respective states.
  • 0 - The open function is not called yet.
  • 1 - The open function is called.
  • 2 - The send function is called.
  • 3 - Receiving the response.
  • 4 - The response is completed.
It's common to do some processing when the readyState is 4. It's also suggested to ignore states other than 4 due to browser-compatibility problems. An asynchronous object's status represents the HTTP response code. The statusText property returns a string representing the status code in a friendly form.

The responseText property returns a string representing the response text from the server. But it should be noticed that, if the server doesn't specify the charset, such as 'Content-Type: text/html; charset=Big5', the default charset of responseText is UTF-8. If the response is XML, use responseXML to get the corresponding DOM object.

The following example demonstrates a simple but complete flow about how to get data asynchronously. The requested document includes Traditional Chinese characters, so it's saved as UTF-8. This example will request the document asynchronously and show the content without refreshing the page.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">
<html>
    <head>
        <meta content="text/html; charset=UTF-8" http-equiv="content-type">
        <script type="text/javascript">
            window.onload = function() {
                var xhr = window.XMLHttpRequest && 
                      (window.location.protocol !== 'file:' 
                          || !window.ActiveXObject) ?
                       function() {
                           return new XMLHttpRequest();
                       } :
                       function() {
                          try {
                              return new ActiveXObject('Microsoft.XMLHTTP');
                          } catch(e) {
                              throw new Error('XMLHttpRequest not supported');
                          }
                       };
                       
                document.getElementById('req').onclick = function() {
                    var request = xhr();
                    request.onreadystatechange = function() {
                        if(request.readyState === 4) {
                            if(request.status === 200) {
                                document.getElementById('table').innerHTML = 
                                    request.responseText;
                            }
                        }
                    };
                    request.open('GET', 'XMLHttpRequest-1.txt');
                    request.send(null);
                };
            };
        </script>        
    </head>
    <body>
        <button id='req'>Get the table</button>
        <div id="table"></div>
    </body>
</html>

I'll talk more about asynchronous request objects. If you are eager for more documentation about XMLHttpRequest, take a look at the following link.