Edit Page

Asynchronous JavaScript (Ajax programming)

Introduction

Asynchronous JavaScript and XML (Ajax) is an approach to using a set of existing web technologies to create asynchronous web applications that are capable of fetching resources over a network and update the web page without reloading the entire page. This makes web applications faster and more responsive to user actions.

Although the X in Ajax stands for XML, JSON is preferred over XML nowadays because of its many advantages such as being more readable, lighter in size and very close to JavaScript with native support for parsing JSON using the built in method JSON.parse.

We will look at how to consume an API in JavaScript using three methods:

  • The XMLHttpRequest API
  • The Fetch API
  • The Fetch API with async/await

We will be using the Giphy API, which at the time of writing this lecture note is version 1.0. You will need to sign up for an account and obtain an API key.

Additionally, we will be using Postman to send HTTP requests and inspect the responses, so we can parse and traverse the returned JSON responses easily.

Using the XMLHttpRequest API

The XMLHttpRequest (XHR) object is used to send HTTP requests over the network to web servers. This API allows us to send requests and retrieve data from a resource without having to do a full page reload. This API helps us create a better user expierence for web applications since we can update parts of a web page without having to reload the whole page and interrupt the user while interacting with our web page.

The XMLHttpRequest.readyState property returns the state an XMLHttpRequest client is in. An XHR client may be in any of the following states:

Syntax

var xhr = new XMLHttpRequest();

XMLHttpRequest states

ValueStateDescription
0UNSENTClient has been created but open() not called yet.
1OPENEDopen() has been called.
2HEADERS_RECEIVEDsend() has been called, and headers and status are available.
3LOADINGProcessing/Downloading; responseText holds partial data.
4DONEThe operation is now complete.

Example

The following example sends an HTTP request to the Giphy API to obtain images for the given keyword. You will need to replace the value of the variable ? with your API key.:

HTML
1
2
3
4
5
    <header>
        <input id="albumIdField" type="text" placeholder="Search GIFs">
        <button id="xhrSearch">Search using XHR</button>
    </header>
    <div id="searchResults"></div>
JS
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
let btnXHR = document.getElementById('xhrSearch');
let searchText = document.querySelector('header input[type="text"]');
let searchResults = document.getElementById("searchResults");

btnXHR.addEventListener("click", function () {
    // clear previous search results
    searchResults.innerHTML = "";
    fetchGiphyAPI_UsingXHR(searchText.value);
});


function fetchGiphyAPI_UsingXHR(keyword) {
    if (!keyword) {
        return;
    }
    var url = "https://api.giphy.com/v1/gifs/search";
    var apiKey = "????????????????";
    var params = "api_key=" + apiKey + "&limit=5&q=" + encodeURIComponent(keyword);
    var xhr = new XMLHttpRequest();
    xhr.addEventListener("readystatechange", function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
            processResponse(JSON.parse(this.responseText));
        }
    });

    xhr.open("GET", url + "?" + params);
    xhr.send();
}

function processResponse(resp) {
    for (item of resp.data) {
        let imgElement = document.createElement("img");
        imgElement.src = item.images.downsized_medium.url;
        imgElement.alt = item.title;
        searchResults.appendChild(imgElement);
    }
}

Using the Fetch API fetch() with Promises

The Fetch API provides an interface for making HTTP requests to access resources across the network. This API provides similar functionalities as the XMLHTTPRequest, so we can make HTTP requests (e.g., using GET, POST methods) to send data, get data, download resources, or upload files. However, the Fetch API provides more powerful and flexible feature set in an easy to read.

Fetch API Syntax

const fetchResponsePromise = fetch(resource [, init]) Where:

  • resource is a string that contains the URL to the resource or a Request object.
  • init is an optional object that contains custom settings for the request such as the HTTP method, headers, credentials (cookies, HTTP authentication entries), etc. For the complete list of options, see the API documentation on MDN.

The fetch() method starts fetching a resource from the network and iimmediatly returns a promise object which is fulfilled once the response is received, which can be obtained inside the .then() method. If a network error is encountered, the promise is rejected and an error is thrown, which can be caught and handled inside the .error() method.

Example

HTML
1
2
3
4
5
    <header>
        <input id="albumIdField" type="text" placeholder="Search GIFs">
        <button id="fetchSearch">Search using fetch</button>
    </header>
    <div id="searchResults"></div>
JS
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
let btnFetch = document.getElementById('fetchSearch');
let searchText = document.querySelector('header input[type="text"]');
let searchResults = document.getElementById("searchResults");

btnFetch.addEventListener("click", function (){
        // clear previous search results
        searchResults.innerHTML = "";
        fetchGiphyAPI_UsingFetch(searchText.value);
});

function fetchGiphyAPI_UsingFetch(keyword) {
    if (!keyword) {
        return;
    }
    var url = "https://api.giphy.com/v1/gifs/search";
    var apiKey = "????????????????";
    var params = "api_key=" + apiKey + "&limit=5&q=" + encodeURIComponent(keyword);
    var requestOptions = {
        method: 'GET'
      };
    fetch(url + "?" + params, requestOptions)
    .then((response) => {
        return response.text();
    })
    .then((data) => {
        processResponse(JSON.parse(data))
    })
    .catch((e) => {
        console.error(e);
    })
}

function processResponse(resp) {
    for (item of resp.data) {
        let imgElement = document.createElement("img");
        imgElement.src = item.images.downsized_medium.url;
        imgElement.alt = item.title;
        searchResults.appendChild(imgElement);
    }
}

Using the Fetch API with async/await

The async/await syntax simplifies the writing of asynchronous code the work with promises by providing a syntax similar to that of writing synchronous code. This syntax is more cleaner style and helps us avoid the need to explicitly write promise chains (e.g., .then().then().then()).

An async function is a function declared with the async keyword, which allows us to use the await keyword within the function.

HTML
1
2
3
4
5
6
    <header>
        <input id="albumIdField" type="text" placeholder="Search GIFs">
        <button id="fetchAsyncAwaitSearch">Search using fetch with async/await</button>

    </header>
    <div id="searchResults"></div>
JS
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
let btnFetchAsyncAwait = document.getElementById('fetchAsyncAwaitSearch');
let searchText = document.querySelector('header input[type="text"]');
let searchResults = document.getElementById("searchResults");

btnFetchAsyncAwait.addEventListener("click", function (){
    // clear previous search results
    searchResults.innerHTML = "";
    fetchGiphyAPI_UsingFetchAsyncAwait(searchText.value)
    .catch((e) => {
        console.error(e);
    });
});

async function fetchGiphyAPI_UsingFetchAsyncAwait(keyword) {
    var url = "https://api.giphy.com/v1/gifs/search";
    var apiKey = "????????????????";
    var params = "api_key=" + apiKey + "&limit=5&q=" + encodeURIComponent(keyword);
    if (!keyword) {
        return;
    }
    var requestOptions = {
        method: 'GET'
    };
    
    const response = await fetch(url + "?" + params, requestOptions); // Wait until the request completes.
    const data = await response.json(); // waits until the response completes
    processResponse(data);
}

function processResponse(resp) {
    for (item of resp.data) {
        let imgElement = document.createElement("img");
        imgElement.src = item.images.downsized_medium.url;
        imgElement.alt = item.title;
        searchResults.appendChild(imgElement);
    }
}

Complete web application using XMLHttpRequest, Fetch and Fetch with async/await

Below are the links for the complete web app that uses the three methods of sending HTTP requests: XMLHttpRequest API, fetch API and the fetch API with async/await:

Additional Examples