Ajax — Async, Callback & Promise

Vikash Agarwal
Frontend Weekly
Published in
12 min readFeb 6, 2016

Ajax is the backbone of Javascript application. It’s used heavily with SPA(Single Page Application). It’s used to communicate with the server. In this blog, I will cover the following topics

  • Asynchronous nature of Javascript (async)
  • AJAX
  • Different ready state of AJAX
  • Callback
  • Promise
  • Cross domain call
  • JQuery Example
  • More Examples of Callback & Promise

In order to fully understand ajax, we need to understand the async nature of javascript and how to deal with the async programming. I am dividing the blog into two parts. First we will understand the async behavior and then we will look at the details of AJAX.

Javascript is full with async programming. Below are some of the examples.

  • AJAX Call
  • setTimeout or setInterval
  • Reading a file
  • Events

Async in Javascript is about two activities, where one activity triggers another activity, which will be completed in future.

For example registering for a click event and waiting for the user to click and/or making a server call to get some data. Its like we need to process something which we don’t have right away, so we wait for it to complete and once it is done, we process it.

Lets see some more example of Async activity in daily life:

Example#1

if you ask me how much is 2+2, I will tell you 4 immediately. But if you ask me how much is 23423423422*23423423 then I will ask you to give me 5 minute to calculate it and after 5 minute, I will tell you the result. After 5 minute you can use the result to do whatever you need to.

Example#2

You went to car service center to repair your car and there is a huge queue in the service center. Service center representative ask you to leave your car and contact number so that, they will call you after inspecting the car. They might inspect your car after 2–3 hours and after inspecting, they will call you to tell the status.

Example#3

You order pizza for home delivery and after 30 minutes, you will hear your door bell.

In all the above examples, you want something which is not ready at the moment. You will have to wait for sometime to get the actual results. Lets see some javascript example now.

Timer Example :-

1. function showSessionExpire(){
2. console.log("Your session expired");
3. }
4. setTimeout(showSessionExpire, 1000);
5. console.log("showSessionExpire will execute after 1000 ms");

In the above code, you are setting up a timer function in line#4. showSessionExpire will execute after 1000 ms. line#4 and showSessionExpire are related but it is not executing immediately. It will execute after 1000 ms. During this 1000 ms gap, browser can either stay idle or do some other activity.

Now Lets see how AJAX works:

AJAX Example :-

1. function makeAJAXCall(methodType, url, callback){
2. var xhr = new XMLHttpRequest();
3. xhr.open(methodType, url, true);
4. xhr.onreadystatechange = function(){
5. if (xhr.readyState === 4 && xhr.state === 200){
6. callback(xhr.response);
7. }
8. }
9. xhr.send();
10. console.log("request sent to the server");
11. }
12. var url = "https://api.github.com/users";13. function renderUsers(data){
14. ...
15. }
16. makeAJAXCall("GET", url, renderUsers);

In this example, we are making ajax call in line # 16 which will call makeAJAXCall function and browser initiates a server request. renderUsers function is called when response comes from the server. makeAjaxCall and renderUsers are related but renderUsers is not executed immediately after makeAjaxCall. I will cover the more detail about ajax in the part-2 of this blog.

Now Lets see a file reader example:

File Reader Example :-

1. var reader = new FileReader();

2. reader.onload = function(e) {
3. var text = reader.result;
4. }

5. reader.readAsText(file, encoding);

Here we are trying to read a file at line #5. Depending on the file size, browser takes the time to read the content and when content is read, it calls the onload function of the reader object. Its an async activity because browser don’t want to block the application while reading the file. Application initiates the file read operation and waits for the browser to read it. When browser gets the content, we can do whatever we want with the file’s content.

Event Listener Example :-

1. var buttonElem = document.getElementById("button");
2. buttonElem.addEventListener("click", onButtonClick);
3. function onButtonClick(){
4. console.log("button is clicked")
5. }

In the above example, we registered a click event on button in line 2 and then when the user clicks the button, the browser will be notified about the click event and it will execute onButtonClick function. Registering for click event listener and execution of the onButtonClick() are related but they are not executed at the same time.

While reading a blog post from Eric Elliott, I came across an awesome video on event loop which explains how browser handles the async operation. Must to watch.

Hopefully idea of async became clear. Now, lets explore AJAX.

AJAX

As I mentioned earlier, AJAX is the backbone of any modern web application today. AJAX stands for Asynchronous Javascript And XML. It was created during 2005 when XML used to be primary format of data exchange between server & client. In 2015 JSON has become the primary data exchange format. therefore XML has nothing to do with the name. AJAX is used to communicate between client and server. Its important to understand how it works, what are the challenges and how to solve them

I am going to cover the following parts

  • How to make AJAX Calls
  • Different ready state
  • How to handle callback
  • Using Promise
  • Cross domain call
  • JQuery Example
var xhr = new XMLHttpRequest();
xhr.open(methodType, URL, async);
xhr.send();

Above three lines are required to send a request to the server.

var xhr = new XMLHttpRequest();

We first make an instance of XMLHttpRequest. Using the instance we can trigger the XHR call and get the response.

xhr.open(methodType, URL, async);

Here we say, that we want to connect to “URL” using method type “methodType”. we will talk to async in a bit. There are four method type namely GET, POST, PUT & DELETE. GET/POST are used normally. Based on the methodType, pass the value. URL is like the address of the server where we want to connect to.

xhr.send();

At this moment, browser will make a HTTPRequest to the server. xhr will hold all the information about that request. like HTTPConnection status, HTTPResponse status and code.

If we need to make two AJAX Call, we need to repeat the three lines.

In order to process the response sent by the server, we need to add callback on the xhr instance.

xhr.onreadystatechange = function(){
if (xhr.readyState === 4){
if (xhr.state === 200){
console.log("received the response", xhr.responseText);
} else {
console.log("error in processing the request");
}
} else {
//console.log("waiting for the response to come");
}
}

onreadystatechange is called on the xhr instance whenever there is a change in the HTTPConnection. Three things to note here.

  • readyState tells about the state of the connection. It has value from 0–4. It changes its value whenever there is any change in the connection and request. the value goes from 0 to 4. when it reaches 3, it means data starts coming from the server and when it reaches 4, it means all the data has come and server has processed the request completely and the connection is closed. At this moment we can check if our request was processed successfully or was there any error.
  • status :- tells us if our request was processed successfully or was there any error. if status is 200, we know our request is processed successfully. Any other code, states that our request has failed.
  • responseText :- responseText property of xhr instance will hold the value which is returned by the server. server can send any kind of data. It can be of any format like XML, JSON, plain text, binary data etc. Depending of data format, we can process the value.

AJAX Example @ https://jsfiddle.net/vikash20186/4shhjk57/

function makeAjaxCall(url, methodType){
var xhr = new XMLHttpRequest();
xhr.open(methodType, url, true);
xhr.send();
xhr.onreadystatechange = function(){
if (xhr.readyState === 4){
if (xhr.status === 200){
console.log("xhr done successfully");
var resp = xhr.responseText;
var respJson = JSON.parse(resp);
} else {
console.log("xhr failed");
}
} else {
console.log("xhr processing going on");
}
}
console.log("request sent succesfully");
}
// git hub url to get btford details
var URL = "https://api.github.com/users/btford";
makeAjaxCall(URL, "GET");

Please note the use of JSON.parse function. It converts string to javascript object. It also has JSON.stringify function which is reverse of JSON.parse.

Using Callback :-

Callback can appear very confusing in the beginning. But it’s very simple when you get the concept right. What is callback and why do we need it in ajax.

What is callback :- Let’s say we have a function F1 which calls F2. F2 is doing some async operation like AJAX. F1 would like to know the result of the ajax call. Now F1 will pass another function say C1 as an additional parameter to F2 which F2 will call after it process the ajax request completely.

Think of it as F1 is taking service from F2 by giving the service details along with C1. When F2 is done with service, it informs F1 by calling C1 with some additional data.

Why do we need callback :- We need callback because we don’t want to duplicate the ajax code every time we need. We want to create a generic ajax function which takes ajax details as input along with callback reference. After completing the call, it calls the callback so that caller can resume with the result of the ajax call.

In the example above, we used makeAjaxCall to get the user details. Now lets say we want to show all the repositories of that user. For that, we need to make another kind of server call to get the repositories list. Clearly we don’t want to write another makeAjaxCall look alike function to perform the server call. We want to use the makeAjaxCall. For Ex

function makeAjaxCall(url, methodType, callback){
var xhr = new XMLHttpRequest();
xhr.open(methodType, url, true);
xhr.send();
xhr.onreadystatechange = function(){
if (xhr.readyState === 4){
if (xhr.status === 200){
console.log("xhr done successfully");
var resp = xhr.responseText;
var respJson = JSON.parse(resp);
callback(respJson);
} else {
console.log("xhr failed");
}
} else {
console.log("xhr processing going on");
}
}
console.log("request sent succesfully");
}
document.getElementById("userDetails").addEventListener("click", function(){
//git hub url to get a user details
var userId = document.getElementById("userId").value;
var URL = "https://api.github.com/users/"+userId;
makeAjaxCall(URL, "GET", processUserDetailsResponse);
});
document.getElementById("repoList").addEventListener("click", function(){
// git hub url to get btford details
var userId = document.getElementById("userId").value;
var URL = "https://api.github.com/users/"+userId+"/repos";
makeAjaxCall(URL, "GET", processRepoListResponse);
});
function processUserDetailsResponse(userData){
console.log("render user details", userData);
}
function processRepoListResponse(repoList){
console.log("render repo list", repoList);
}

JS Fiddle Link :- https://jsfiddle.net/vikash20186/w0bfhwgd/

In the above example, you can see that there are two places where makeAjaxCall function is called and handling of server response is different for the both the scenario. makeAjaxCall is a kind of service function here which takes the ajax details along with callback reference. When it completes the ajax call, it informs the caller by calling its callback reference. Using the callback reference, we can create a reusable independent function which can just focus on making ajax call. In the callback function, we can process the data such as show the user details or listing down the repositories list.

We can use the ajax service function at n number of places by passing ajax call details like URL, method and callback reference. Callbacks are great way to separate the core logic of ajax with the rest of the application. But unfortunately, it becomes very difficult to handle callback when we do series of ajax calls where one call is dependent on previous call. We might encounter difficulty in maintaining multiple callback references and handling multiple success and error conditions. Promise is a better way to manage multiple ajax calls. Let’s explore Promise next.

Promise :-

Promise is used to overcome issues with multiple callbacks and provide better way to manage success and error conditions. Promise looks little complex in the beginning but its very simple and effective to deal with. Promise is an object which is returned by the async function like ajax. Promise object has three states

  • pending :- means the async operation is going on.
  • resovled :- async operation is completed successfully.
  • rejected :- async operation is completed with error.

There are two parts using a promise object. Inside async function (Part1) and where its called (Part2).

Part1 — Inside Async function,

  • Promise object is created.
  • Async function returns the promise object
  • If async is done successfully, promise object is resolved by calling its resolve method.
  • If async is done with error, promise object is rejected by calling its rejected method.

Part2 — Outside Async function

  • Call the function and get the promise object
  • Attach success handler, error handler on the promise object using then method

Lets look at some promise code below.

//Part 1 - Inside Async function
function makeSomeAsyc(){
//Create Promise object
var promiseObj = new Promise(function(fullfill, reject){
... //Add ajax code here.
... // on success, call fullfill method, to resolve
... // on error, call reject method, to reject
});
//Returns Promise object
return promiseObj;
}
function handleSuccess() { ... }
function handleError() { ... }
//Part 2- Outside Async function
var pobj = makeSomeAsyc();
//Attaching success handler to promise
pobj.then(handleSuccess, handleError);

Lets use promise object in our makeAjaxCall function.

function makeAjaxCall(url, methodType){
var promiseObj = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.open(methodType, url, true);
xhr.send();
xhr.onreadystatechange = function(){
if (xhr.readyState === 4){
if (xhr.status === 200){
console.log("xhr done successfully");
var resp = xhr.responseText;
var respJson = JSON.parse(resp);
resolve(respJson);
} else {
reject(xhr.status);
console.log("xhr failed");
}
} else {
console.log("xhr processing going on");
}
}
console.log("request sent succesfully");
});
return promiseObj;
}
document.getElementById("userDetails").addEventListener("click", function(){
// git hub url to get btford details
var userId = document.getElementById("userId").value;
var URL = "https://api.github.com/users/"+userId;
makeAjaxCall(URL, "GET").then(processUserDetailsResponse, errorHandler);
});
document.getElementById("repoList").addEventListener("click", function(){
// git hub url to get btford details
var userId = document.getElementById("userId").value;
var URL = "https://api.github.com/users/"+userId+"/repos";
makeAjaxCall(URL, "GET").then(processRepoListResponse, errorHandler);
});
function processUserDetailsResponse(userData){
console.log("render user details", userData);
}
function processRepoListResponse(repoList){
console.log("render repo list", repoList);
}
function errorHandler(statusCode){
console.log("failed with status", status);
}

Example @ https://jsfiddle.net/vikash20186/w0bfhwgd/7/

If you notice, makeAjaxCall function returns a promise object and doesn’t take any callback. The promise object is resolved when the data comes from the server or is rejected in terms of failure. We can attach our handlers to the promise object. Promise object can be resolved or rejected only one time. We can add multiple success and error handlers on the promise object. Each one will be called in the same order as they are registered.

Callback becomes messy when you want to write complex async code. Like doing sequence of AJAX calls. You can read more about Javascript Promise @ http://www.html5rocks.com/en/tutorials/es6/promises/

Cross Domain Call :-

AJAX has a limitation that it cannot make an AJAX request to the cross domain server by default. Depending on the setup you might encounter cross domain call issue, so you should know how to handle it.

Cross Domain :- In the example above, we are using the API given by api.github.com and we are using jsfiddle to execute our UI code. Here our javascript code lives at jsFiddle server and its trying to get data from github server. This is cross domain.

By Default, AJAX cannot make cross domain call, browser will reject the calls to the different domain. In order to make cross domain call there are two options

  • Using CORS
  • Using JSONP

Both the options requires some server changes. It cannot be done purely using javascript.

CORS is the new way to deal with cross origin AJAX request. github api are CORS enabled. In order to enable CORS, response should contain Access-Control-Allow-Origin header with the domain value or * to work for all. Github has set as *.

JSONP can also be used if CORS cannot be enabled by server or for old browsers. JSONP actually uses script tag to get the data from the server. Script is allowed to be fetched from any domain, So in JSONP, we need to create a script with the url as src and the server has to wrap the response in a callback function. Response sent by server is actually a javascript code which contains data inside a wrapper function. In JSONP, there is no ajax call being made.

Read extensive tutorial on CORS @ http://www.html5rocks.com/en/tutorials/cors/

AJAX with JQuery

jquery provides an easy way to deal with ajax. With jQuery our makeAjaxCall function would be.

JQuery.ajax with callback

function makeAjaxCall(url, methodType, callback){
$.ajax({
url : url,
method : methodType,
dataType : "json",
success : callback,
error : function (reason, xhr){
console.log("error in processing your request", reason);
}
});
}
// git hub url to get btford details
var URL = "https://api.github.com/users/btford";
makeAjaxCall(URL, "GET", function(respJson){
document.getElementById("userid").innerHTML = respJson.login;
document.getElementById("name").innerHTML = respJson.name;
document.getElementById("company").innerHTML = respJson.company;
document.getElementById("blog").innerHTML = respJson.blog;
document.getElementById("location").innerHTML = respJson.location;
});

JS Fiddle Link :- https://jsfiddle.net/vikash20186/4shhjk57/5/

$.ajax is used to make ajax call. There are other variations which jQuery provides. You should check their documentation. Here we are using callback approach with jquery. We don’t have to check for readyState and status. JQuery does all that and calls success callback if call succeed or error callback if it fails. Jquery also support promise out of box. $.ajax returns a promise object, so that we don’t have to pass a callback function.

JQuery.ajax with promise

function makeAjaxCall(url, methodType, callback){
return $.ajax({
url : url,
method : methodType,
dataType : "json"
})
}
// git hub url to get btford details
var URL = "https://api.github.com/users/btford";
makeAjaxCall(URL, "GET").then(function(respJson){
document.getElementById("userid").innerHTML = respJson.login;
document.getElementById("name").innerHTML = respJson.name;
document.getElementById("company").innerHTML = respJson.company;
document.getElementById("blog").innerHTML = respJson.blog;
document.getElementById("location").innerHTML = respJson.location;
}, function(reason){
console.log("error in processing your request", reason);
});

JS Fiddle Link :- https://jsfiddle.net/vikash20186/4shhjk57/6/

Learn more about JQuery Promise here.

To summarize, in order to understand ajax you should know

  • Async nature of javascript
  • Use of Callback & Promise with and without jquery

Promise is the right way to deal with async activity. Its clean and very flexible. It makes your application maintainable and you can extend the functionality very easily. Callback is easy to start with but certainly not the right direction. If you are new to javascript, you should know callback but should use promise.

By now, you should know the following

  • What is async operation in javascript
  • How does event looping works
  • How does ajax works with & without jquery
  • What is promise, how to use it & why is it required
  • How to make cross domain call
  • How does JSONP works

--

--