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
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:
'load'
event.The XMLHttpRequest object also has a number of properties that are helpful:
readyState
- the current state of the propertyresponse
- the body of the response, an ArrayBuffer
, Blob
, Document
, or DOMString
based on the value of the responseType
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 terminatedThe 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 longSeveral of these events have properties you can assign a function to directly to capture the event:
onerror
- corresponds to the error
eventonload
- corresponds to the load
eventonloadend
- corresponds to the loadend
eventonloadstart
- corresponds to the loadstart
eventonprogress
- corresponds to the progress
eventontimeout
- corresponds to the timeout
eventIn 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
}
});
Of course the point of learning about the XMLHttpRequest object is to perform AJAX requests. So let’s turn our attention to that task.
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.
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
});
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.
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');
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();
Info
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.