Callback, Promises and Async/Await — Asynchronous code in Javascript
By: Aravind S
Tags: Promises, Asyncawait, JavaScript, Callback, Callback Hell

Photo by Markus Spiske on Unsplash

JavaScript program is single threaded and all code is executed in a sequence, not in parallel. An example goes here

const second = () => {

console.log('Second');

}

const first = () => {

console.log('First');

second();

console.log('The End');

}

first();

//Results

First

Second

The End

With synchronous, all the instructions are executed one after the other, line by line just in the order that they appear in the code.

Callbacks:

Now let’s add some asynchronous code here. we’re going to use the setTimeout function, which is basically to set a timer in JavaScript.

const second = () => {

setTimeout(() => {

console.log('Second Async');

}, 2000);

}

const first = () => {

console.log('First');

second();

console.log('The End');

}

first();

//Results

First

The End

Second Async

Now when the first function gets called, logs “First” to the console, and then calls the second function. Now, this function calls the Set Timeout function, which is basically like a timer that will execute the callback function that we passed into it, after 2000 milliseconds. However this will not make the code stop for two seconds, but instead the function returns, goes back to the first function and logs “The end”. Then after the two seconds actually have passed ”Second Async” is logged to the console. We do not wait for a function to finish its work, and then do something with the result. Instead, we let that function do its job in the background so that we can move on with the code execution. We then also pass in a callback function, that will be called as soon as the main function has done whatever it had to do.

Callback Hell:

The problem with callbacks is that it creates Callback Hell.

We will start nesting functions within functions and it starts to get really hard to read the code. Here is an example of the callback hell.

function getData() {

setTimeout(() => {

getData1();//Async Call.

setTimeout(() => {

getData2();//Async Call.

setTimeout(() => {

getData3();

console.log("Callback Hell");

}, 1500);

}, 1500);

}, 1500);

}

getData();

Promises:

In ES6, something called “promises” was introduced. And with promises, we can avoid all of this callback hell here, and have a nicer and cleaner syntax

when using asynchronous JavaScript. A promise is an object that keeps track about whether a certain event has happened already or not, and if it did happen, then the promise determines what happens next. And with events here I mean an asynchronous event like a timer finishing or data coming back from an ajax call. A promise can have different states. Before the event has happened the promise is pending. Then after the event has happened the promise is called settled or resolved. Now, when the promise was actually successful, which means that a result is available, then the promise is fulfilled, but if there was an error then the promise is rejected.

function onSuccess () {

console.log('Success!')

} function onError () {

console.log('Fail')

} const p = new Promise((resolve, reject) => {

setTimeout(() => {

resolve()

}, 2000)

}) p.then(onSuccess).catch(onError)

Both .then and .catch will return a new promise., which means that promises can be chained. Just so you can see one more example, here’s a common use case when you use the fetch API. fetch will return you a promise that will resolve with the HTTP response. To get the actual JSON, you’ll need to call .json . Because of chaining, we can think about this in a sequential manner.

fetch('/api/user.json')

.then((response) => response.json())

.then((user) => {

// user is now ready to go.

})

Async/Await:

Now we know how to construct, and consume promises. But, this syntax to consume promises can still be quite confusing and difficult to manage.

In ES8 or ES2017, something called Async/Await was introduced to the JavaScript language, in order to make it a lot easier for us developers

to consume promises.

async function getPromise(){

const data = await getData();

}

getPromise()

If the async function returns a value, that value will also get wrapped in a promise. That means you’ll have to use .then to access it.

async function add (x, y) {

return x + y

}

add(2,3)

.then((result) => {

console.log(result) // 5

})

With Async/Await, the most common approach for error handling is to wrap your code in a try/catch block.

try{

async function add (x, y) {

return x + y

}

add(2,3)

.then((result) => {

console.log(result) // 5

})

}catch(e){

console.log(e)

}

Happy Reading!