Asynchronous programming becomes unavoidable in todays programming. In this article, I would like to talk about, how Javascript evolved to handle asynchronous programming using callbacks. What is a callback?

Consider a scenario of a candidate who attends an interview in a reputed organisation. Once the interview is over the HR told that they will process the feedback and get back to him. Now the candidate would go back to his current job and continue his routine life until he get a callback from the new organisation. He would not wait at the gates of that company until he get a response. Once he get the callback he would act upon it. The callback could be positive or negative and the candidate will take action accordingly.

In computer programming, a function need not to wait for the other function to return immediately. The other function could be a long running process or the response time of the function might not be predictable. So waiting for the function to return is not efficient. Instead it can go back to other part of program and continue the execution. But it should give a handler to the long running function, so that it can connect to the caller once it is done with the long running process. Similar to the candidate who gave the contact information to the interviewer so that they can give a callback.

Consider a scenario where you want to get the list of user reviews.

getUserReviews(function (reviews) {
// Process the reviews
});

Say getUserReviews is a function that will hit an endpoint and invoke the success callback once it got the response. This is the common way to achieve asynchronous programming in Javascript. Though callbacks are popular even in modern Javascript, it has a major drawback in complex scenarios. Things cannot be simple as above always.

In the above example we cannot just get user reviews. In a typical e-commerce website, we see user reviews for products. So in this case, we need to get the list of products first before fetching user reviews. So code goes like this.

getProducts(function (products) {
getUserReviews(products, function (reviews) {
// Process the reviews
});
})

From the list of products received, we fetch user reviews. We are not yet done. To fetch the list of products we need the user to be signed in. So we may have to pass the authentication token to every request. Things started to getting a little complex now.

authenticate(user, password, function (token) {
getProducts(token, function (products) {
getUserReviews(token, products, function (reviews) {
// Process the reviews
});
})
})

Look at the number of indentation levels. Things could go worse in complex situations. When you have lots of callback functions in your code! it gets harder to work with them, the more of them you have in your code and it gets particularly bad when you need to do loops, try-catch blocks and things like that. This term is often referred to as Callback Hell.

Promises - the Rescuer

One of the approach to avoid callback hell is to use Promises. After the introduction of Promise pattern, it is easier to achieve asynchronous programming with simple and readable code. What is promise in Javascript?

Bob once promised to get a Playstation 4 as a birthday gift to his son Bruce. Bruce was so excited. But Bruce didn't get the PS4 yet, but he just got the promise from his father. Now with all the excitement he get back to his routine life. On Bruce's birthday Bob may fulfil his promise or he may not. If he fails to fulfil the promise he has to give a reason to Bruce on that.

In Javascript programming, Promise is an object that may produce a single value in future. The single value could be the expected value or a reason why it cannot produce the expected value.

Our above example of getting the user reviews can be rewritten as below. Assuming all those functions will return a Promise object instead of accepting callback function as argument.

authenticate(user, password)
.then(token => getProducts)
.then((token, products) => getUserReviews)
.then((token, reviews) => {
// Process the reviews
})

The code becomes very simple and readable. The catch function let us handle any error. Another good thing about promise is that the response handlers can be chained. A call to promise.then returns a promise, so that we can call the next .then on it. When a handler returns a value, it becomes the result of that promise, so the next .then is called with it.

But if you notice, then and catch functions still use callbacks as argument. That is not a problem. The code is still readable. But ES6 added syntactic sugar for promises that makes the code more readable and simple.

Async / Await

Async/Await is a special syntax introduced in ES6 to make Promise code more readable and simple. To use await keyword inside a function, the function has to be marked as async.

When Javascript engine encounter a function call with await statement, it will queue the execution of that function and get back to the caller function. This means that the statements below the awaited function will not be executed until the awaited function resolved to a value.

function callerFunction(){
asyncFunction();
// Below functions will be called right after function1
function2();
function3();
}

async function asyncFunction() {
await function1();
// Below functions cannot be called until function1 resolved/rejected.
function4();
function5();
}

Not all the functions can be marked as await. As I already said async/await is just a syntactic sugar for promises. So it is still the promises which is doing all the magic. To mark a function as await, the function should return a promise object. If a function returning promise is not awaited, it will directly a promise object. That can be either awaited or handled via then and catch functions.

Following two pieces of code does exactly the same thing.

try {
var users = await getUsers();
console.log(`${users.length} users found`);
} catch (error) {
console.log(error);
}
var promiseObject = getUsers();
promiseObject
.then(users => console.log(`${users.length} users found`))
.catch(error => console.log);

So async functions are the ultimate solution to write more readable asynchronous code. To understand more about async / await, I would recommend you to read more about Javascript Promises. Thanks for reading !!!