Asynchronous JavaScript and XML (AJAX) is a term coined by Jesse James Garrett to describe a technique of using the XMLHttpRequest object to request resources directly from JavaScript. As the name implies, this was originally used to request XML content, but the technique can be used with any kind of data.
The XMLHttpRequest
The XMLHttpRequest
object is modeled after how the window
object makes web requests. You can think of it as a state machine that can be in one of several possible states, defined by both a constant and an unsigned short value:
- UNSENT or 0 The client has been created, but no request has been made. Analogous to a just-opened browser before you type an address in the address bar.
- OPENED or 1 The request has been made, but the response has not been received. The browser analogue would be you have just pressed enter after typing the address.
- HEADERS_RECEIVED or 2 The first part of the response has been processed. We’ll talk about headers in the next chapter.
- LOADING or 3 The content of the response is being downloaded. In the browser, this would be the stage where the HTML is being received and parsed into the DOM.
- DONE or 4 The resource is fully loaded. In the DOM, this would be equivalent to the
'load'
event.
XMLHttpRequest Properties
The XMLHttpRequest object also has a number of properties that are helpful:
readyState
- the current state of the propertyresponse
- the body of the response, anArrayBuffer
,Blob
,Document
, orDOMString
based on the value of theresponseType
responseType
- the mime type of responsestatus
- returns an unsigned short with the HTTP response status (or 0 if the response has not been received)statusText
- returns a string containing the response string fro the server, i.e."200 OK"
timeout
- the number of milliseconds the request can take before being terminated
XMLHttpRequest Events
The XMLHttpRequest object implements the EventTarget
interface, just like the Element
and Node
of the DOM, so we can attach event listeners with addEventListener()
. The specific events we can listen for are:
abort
- fired when the request has been aborted (you can abort a request with the XMLHttpRequest.abort() method)error
- fired when the request encountered an errorload
- fired when the request completes successfullyloadend
- fired when the request has completed, either because of success or after an abort or error.loadstart
- fired when the request has started to load dataprogress
- fired periodically as the request receives datatimeout
- fired when the progress is expired due to taking too long
Several of these events have properties you can assign a function to directly to capture the event:
onerror
- corresponds to theerror
eventonload
- corresponds to theload
eventonloadend
- corresponds to theloadend
eventonloadstart
- corresponds to theloadstart
eventonprogress
- corresponds to theprogress
eventontimeout
- corresponds to thetimeout
event
In addition, there is an onreadystatechange
property which acts like one of these properties and is fired every time the state of the request changes. In older code, you may see it used instead of the load
event, i.e.:
xhr.onreadystatechange(function(){
if(xhr.readyState === 4 && xhr.status === 200) {
// Request has finished successfully, do logic
}
});
Using AJAX
Of course the point of learning about the XMLHttpRequest object is to perform AJAX requests. So let’s turn our attention to that task.
Creating the XMLHttpRequest
The first step in using AJAX is creating the XMLHttpRequest object. To do so, we simply call its constructor, and assign it to a variable:
var xhr = new XMLHttpRequest();
We can create as many of these requests as we want, so if we have multiple requests to make, we’ll usually create a new XMLHttpRequest object for each.
Attaching the Event Listeners
Usually, we’ll want to attach our event listener(s) before doing anything else with the XMLHttpRequest
object. The reason is simple - because the request happens asynchronously, it is entirely possible the request will be finished before we add the event listener to listen for the load
event. In that case, our listener will never trigger.
At a minimum, you probably want to listen to load
events, i.e.:
xhr.addEventListener('load', () => {
// do something with xhr object
});
But it is also a good idea to listen for the error
event as well:
xhr.addEventListener('error', () => {
// report the error
});
Opening the XMLHttpRequest
Much like when we manually made requests, we first need to open the connection to the server. We do this with the XMLHttpRequest.open() method:
xhr.open('GET', 'https://imgs.xkcd.com/comics/blogofractal.png');
The first argument is the HTTP request method to use, and the second is the URL to open.
There are also three optional parameters that can be used to follow - a boolean determining if the request should be made asynchronously (default true
) and a user and password for HTTP authentication. Since AJAX requests are normally made asynchronously, and HTTP authentication has largely been displaced by more secure authentication approaches, these are rarely used.
Setting Headers
After the XMLHttpRequest
has been opened, but before it is sent, you can use XMLHttpRequest.setRequestHeader() to set any request headers you need. For example, we might set an Accept
header to image/png
to indicate we would like image data as our response:
xhr.setRequestHeader('Accept', 'image/png');
Sending the XMLHttpRequest
Finally, the XMLHttpRequest.send() method will send the request asynchronously (unless the async
parameter in XMLHttpRequest.open()
was set to false
). As the response is received (or fails) the appropriate event handlers will be triggered. To finish our example:
xhr.send();
A second major benefit of the JQuery library (after simplifying DOM querying and manipulation) was its effort to simplify AJAX. It provides a robust wrapper around the XMLHttpRequest
object with the jQuery.ajax() method. Consider the AJAX request we defined in this chapter:
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', () => {
// do something with xhr object
});
xhr.addEventListener('error', () => {
// report the error
});
xhr.open('GET', 'https://imgs.xkcd.com/comics/blogofractal.png');
xhr.setRequestHeader('Accept', 'image/png');
xhr.send();
The equivalent jQuery code would be:
jQuery.ajax("https://imgs.xkcd.com/comics/blogofractal.png", {
method: 'GET',
headers: {
Accept: 'image/png'
},
success: (data, status, xhr) => {
// do something with data
},
error: (xhr, status, error) => {
// report the error
}
});
Many developers found this all-in-one approach simpler than working directly with XMLHttpRequest
objects. The W3C adopted some of these ideas into the Fetch API.