Select Page

Understand Callback functions | Javascript Promises and async-await

Written by Asbar Ali

This article will be useful if you’re relatively new to javascript, help you understand why you need to write asynchronous code in JavaScript, callbacks are a thing, cause us hell but are required, javascript promises, async-await and so on. If you’re an experienced JS developer, give some fresh insights on how these things work.

There were developers who program in javascript with callbacks but after a while, there has been promise in javascript. It was part of certain libraries that you could import and add to your code.  Now in the latest version of javascript, promises are native to javascript work. Async-await provides special syntax to work with promise in a comfortable way and it’s easy to understand and use.

As a JavaScript developer, It’s important to understand what really happens under the hood. So the first part of the article is mainly focused on the concept of ‘Event loop’.


01. What is Event Loop in JavaScript?

We all know javascript is single-threaded and blocking in nature. There is a feature called ‘even loop’ provided by the environment such as web browsers, nodeJs and so on. So it has the capability for asynchronous execution providing non-blocking functionality. Let’s take a real-life example of how Javascript works with async executions.

If you go to watch a movie and there’s like 3 people ahead of you.  They have the tickets ready and they let them in. When the time comes for you and say to the ticket checker  “I don’t have my ticket with me and my wife is bringing. She is on her way and will be here any second. So can we just wait?”.

Then the time checker says “No I can’t do that buddy. There’s a line behind you and you need to step aside and when she comes back you can get in the line”.

That’s how javascript actually behaves with async execution. If something needs to wait, it will make you wait on the side.

If you look at JavaScript runtime itself like V8 which is the Runtime inside Chrome, the simplified view will look like this.

If clone the V8 engine codebase and grep for things like setTimeout, DOM or HTTP request, they’re not in there and not exist in V8 which makes you a surprise. We call these things as WebAPI which are extra things that the browser provides.

Before move into deep, you should have basic understand about javascript single thread and then let move back to the event loop.

JavaScript Single Thread:  JavaScript Single Thread means the main thread on which the javascript code executed in one line at a time and there is no possibility of doing stuff in parallel.

For example, let take this function. This takes the number of seconds as an argument and executes for those number of seconds.  If you call this function 3 as the input, the main JS thread will be blocked for the next 3 seconds and cannot do anything else.

function delayBySeconds(sec) {
	let start = now = Date.now();
	while((now - start) < (sec* 1000)) {
		now  = Date.now();
	}
}

delayBySeconds(3);

document.getElementById('blocking').innerHTML = 'Finished Executing';

 

If there is a button on the screen and right-clicking on it and it would not response for the next 5 seconds. That would be quite a bad user experience.

Let’s see another example of whether the single-threaded nature of javascript can really put add this advantage.  Consider making a network request. What if code to handle the network request in a synchronous blocking way. It would look something like this.

makeNetworkRequestCall()

While (!networkRequestReturned()) {
	// just keep looping
}

// runs after request returned 

This code blocks the line of execution at the condition to check whether the network request has returned and in the meanwhile, the main thread or run time will remain block without making any other additional request or user inputs.

So Javascript runtime can do one thing at a time and it can’t make an Ajax request or setTimeOut while other code runs but browser act more than the run time. Look at the complete image of how Stacks, web APIs and callback queue interact with each other.

JavaScript run the code line by line and add functions to the call stack.  If you execute the following code in the gif below, the js engine is going to add each call to the stack. But there is an asynchronous function is here, that’s javascript setTimeout(). These kinds of functions are managed by the event loop.

When the JS engine starts to execute the setTimeout function, the browser kicks off a timer for you. Other code will pop off the stack while setTimout() is in the web APIs. Web APIs can’t modify the code and web APIs pushes the callback to the task queue when it’s finished.

So what event loop does? If the stack is empty, It takes the first thing on the queue and pushes it on to the stack which effectively runs it.

Now check the gif below that I stole from Philip Roberts JS Conference 😛


02. Why do we need Callback Functions?

Let’s do the kind of mimic dummy memes on a server.

Without callbacks – In the following example, there is an array of 2 memes and they’re objects contain title and body attributes. In the getMemes() function, we’re mimicking how it is to fetch from a server and that could take a couple of seconds.

So here, I used setTimout which takes the callback function and it takes 2 seconds time delay whatever you put inside the callback function. The purpose of this function is to get all memes and put them on the page.

There is another function called createMeme() which takes a new Meme as a parameter. Use setTimeout here as well because we’re hypothetically dealing with the server. At there, I push a new Meme to the array.

Simply, we get all the mems and create another new meme.

const memes = [
 {title: 'meme one', body: 'Body of meme one'},
 {title: 'meme two', body: 'Body of meme two'},
];

function getMemes () {
	setTimeout(function(){
  		let output = '';
      	memes.forEach(function(meme, index){
      	output += `<li>${meme.title}</li>`
      });
      
      document.body.innerHTML = output;
  }, 2000)
}

function createMemes(newMeme) {
	setTimeout(function() {
  	memes.push(newMeme);
  }, 3000);
}

getMemes();
createMemes({title: 'meme Three', body : 'Body of meme Three'});

Meme one and Meme two are there, but where is Meme three? It is because createMemes() took longer than getMemes()getMemes() happen in 2 seconds and createMemes() took 3 seconds to execute. We can’t do anything beyond that point.

This is where asynchronous programming comes in and callbacks come in which is oneway to handle it. Now let’s make above it using callbacks.

With Callbacks – 

const memes = [
 {title: 'meme one', body: 'Body of meme one'},
 {title: 'meme two', body: 'Body of meme two'},
];

function getMemes () {
	setTimeout(function(){
  		let output = '';
      	memes.forEach(function(meme, index){
      	output += `<li>${meme.title}</li>`
      });
      
      document.body.innerHTML = output;
  }, 2000)
}

function createMemes(newMemes, callback) {
	setTimeout(function() {
  	memes.push(newMemes);
    callback();
  }, 3000);
}

createMemes({title: 'meme Three', body : 'Body of meme Three'},getMemes);

03. Why do we choose to use Promise in Javascript?

The previous solution to synchronize async call was via nested calls. It was a really simple approach for asynchronous javascript tasks, but can’t scale due to the issue called ‘callback hell’

The drawback of callback pattern – When handling sequential async behaviour, it often hard to read, debug or maintain. This referred to as callback hell.

Look at the following example where callbacks are used to verify the user.

const verifyUser = function(username, password, callback){
   dataBase.verifyUser(username, password, (error, userInfo) => {
       if (error) {
           callback(error)
       }else{
           dataBase.getRoles(username, (error, roles) => {
               if (error){
                   callback(error)
               }else {
                   dataBase.logAccess(username, (error) => {
                       if (error){
                           callback(error);
                       }else{
                           callback(null, userInfo, roles);
                       }
                   })
               }
           })
       }
   })
};

OMG!

Callbacks are everywhere.  If you run the code and it works well but this is not sustainable. I can keep going with callbacks and code gets intended more when writing more code. This is not good.

Callback really makes sense for the idea of an event.  For example, when the mouse is pressed and then trigger a function.

But if I want to sequence asynchronous things that happen in my program, it’s not a good idea. It means, I’m making all the API requests, asking for one thing first, another thing again and blah blah. Then callbacks can get very unwieldy. So we call it a callback hell.

So could be there something better?. Yeah! It’s JavaScript Promises.


04. What is promise in Javascript

A promise is an object that guarantees to deliver a single value. It can be either resolved value or a reason that it’s not resolved.

This is what looks like with callback. (Without JavaScript Promises)

loadjson(url, callback);

Let assume we fetch the data from a specific endpoint. So I use a function called fetch and this is native to javascript in the browser that supports javascript promises.

Let promise = fetch(url);

Once I have that promise as an object and I can do all sorts of things with it. Simply, Promise is an object that can be in a certain state.  All Promises start off in the pending state and then at some unknown point in the future. They may transition to either fulfilled or rejected state. A Promise can have mainly 3 possible states,

(1) Pending – Waiting to get data back from the API

(2) Fulfilled – It means, successfully resolved. We have the results back from the API.

(3) Rejected – Some error has happened. Eg:- Network error for not resolved.

If you can take as Promise object, don’t need to query the promise continuously. Simply use then() and catch() methods.

(01) then() – Receives a function to be executed when it has been fulfilled. It’s a kinda callback function.

(02) catch() – Receives a function to be executed when it has been rejected.

This diagram is from the Mozilla javascript documentation page. Promise checks all the stuff happen from the pending state but it, in the end, there is just then() or catch().

Trulli

Thumb rules of Javascript promises

1) Use promises if you have to use an asynchronous blocking code.

2) Use resolve() in .then method and reject() in .catch method for all practical purposes.

3) Don’t forget to write .then and .catch methods for all the javascript promises.


05. What is Javascript fetch API?

Before looking at the usage of promise, let know about fetch. When it comes to making HTTP requests inside JavaScript code, you can use a variety of different options.

(01) XML HTTP requests
(02) Use a third-party library

What is Fetch? – Fetch is a function but promise-based HTTP request API. It allows you to fetch data, images, from all sorts of different kinds of places and do stuff with it. 

const result = fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => response.json())
  .then(json =>  document.getElementById("demo").innerHTML = json.title);
  

Fetch returns a promise and the things that come from fetch should be converted into an object that we can use. That’s what I do with first then() method. In the second then(), will then have access to the response.

Note: response.json() also returns a promise.


06. Javascript promise example

What actually happens here in the following example?

(1) Fetch post from the API where id=1
(2) Take the response and convert into JSON
(3) Once I have the JSON, display the title of the post and fetch the first comment of the post using the API.

(4) Then take that response and convert it into JSON.
(5) Take the response and display the first name of the comment.
(6) Here, I no longer can be used as short handy as the above example because I have 2 lines of code in most then() methods.

const result = fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then((response)=> {
  	return response.json()})
  .then((json) => {
  	document.getElementById("post").innerHTML = 'Post - ' + json.title
    	return fetch('https://jsonplaceholder.typicode.com/comments?postId=1')
  }).then((response)=> {
  		return response.json()})
   .then((json) => {
    	document.getElementById("comment").innerHTML = 'First Comment for post - ' + json[0].name;
  }).catch((error)=> {
  	console.log(error);
  });

Why used return  – Javascript promises are chainable. In here, fetch and response.json() returns the promises. So you have to explicitly return to continue to all the then() method in this chain of promises.

Handling Errors – Error could happen and it could break either one of API URL is wrong. catch() method will catch any error and this is much nicer than having separate error callbacks for every single thing we do.


07. Promise.all, Promise. race, and promise.allSettled

If you have a lot of different promises, don’t have to keep having tons of .then() methods. Instead, you can use one following method according to your requirements.

(i) Promise. all() – This also returns the promise but fulfills only when all the promises are passed. If one promise is rejected,  promise.all() returns rejected with reason.

In this example, a bunch of promises is created in a different way. These are all starting to run at the same time. If promise4 takes long time than others, they have to wait until promise4 is finished. It is going to take time for the longest promises takes.

const promise1 = Promise.resolve("Hello Promise!!");
const promise2 = 200;
const promise3 = new Promise(function(resolve, reject){
	setTimeout(resolve, 1000, 'Not done yet?');
});
const promise4 = fetch('https://jsonplaceholder.typicode.com/todos').then(function(res){
	return res.json();
});

// Promise all method
Promise.all([promise1, promise2, promise3, promise4])
	.then(function(values){
  
  	let finalValue = '';
    
    values.forEach(function(item, index){
    	let stringifiedValue = JSON.stringify(item);
    	finalValue += `${index} - ${stringifiedValue}</br>`;
    })
 		
    	console.log(values);
  	document.getElementById('promiseAll').innerHTML = finalValue;
  });

(ii) Promise. race() – This is the same as promise.all() except for it’ll returns as soon as the first one is completed instead of waiting for everything to complete. It returns only one value.

const promise1 = fetch('https://jsonplaceholder.typicode.com/todos').then(function(res){
	return res.json();
});
const promise2 = Promise.resolve("I'm promise2 :) but fastly resolved");
const promise3 = 200;
const promise4 = new Promise(function(resolve, reject){
	setTimeout(resolve, 1000, 'Not done yet?');
});

// Promise race method
Promise.race([promise1, promise2, promise3, promise4])
	.then(function(values){
  	document.getElementById('promiseAll').innerHTML = `1- ${values}`;
  });

(ii) Promise.allSettled() – Returns a Promise that resolves after every promise has returned success or failure.

const promise1 = Promise.resolve("Hello Promise!!");
const promise2 = 200;
const promise3 = new Promise(function(resolve, reject){
	setTimeout(resolve, 1000, 'Not done yet?');
});
const promise4 = fetch('https://jsonpliiiaceholder.typicode.com/todostest').then(function(res){
	return res.json();
});

// Promise all settled method
Promise.allSettled([promise1, promise2, promise3, promise4])
	.then(function(values){
  
  	let finalValue = '';
    
    values.forEach(function(item, index){
    	let stringifiedValue = JSON.stringify(item);
    	finalValue += `${index} - ${stringifiedValue}</br>`;
    })
 		
    	console.log(values);
  	document.getElementById('promiseAll').innerHTML = finalValue;
  });

08. Create your own Promises in JavaScript

Those of you who aren’t too familiar or comfortable with promises, after reading this part and you can able to create own promises. I guarantee everything will start to make a lot more sense.  So we’re going to see 3 examples here.

(01) Using a promise to divide some numbers
(02) Wrapping set timeout function inside a promise.
(03) Connecting to a MySQL database using a promise.

(01) Using a promise to divide some numbers

Let’s see the first example of division right here and begin by defining a new function called `divide` which takes in 2 arguments which will be two numbers a and b. we’re going to return a divided by b. pretty straightforward.

function divide(a, b) {
	return a / b;
}

document.getElementById("ownPromise").innerHTML = divide(20, 2);

If set the result in innerHTML and we should see 10 divided by 2 which equals 5. We get 5 right there. Okay so let’s start to convert this into a promise.  This time we’re going to use .then() method and taking the function here which contains the result as the first parameter.

This example doesn’t really require promises because it’s not synchronous but it’s just a basic example of how javascript promises actually work.  So once the bet is done, use .catch() method once again. We can console log the error with the division. Then we can just simply once again log the error itself. This is the basic syntax of the use of a promise.

divide(20, 2).then(function(result){
	document.getElementById("ownPromise").innerHTML = `Division Success: ${result}`;
}).catch(function(error){
  	document.getElementById("ownPromise").innerHTML = `There was an error with division`;
});

Let’s convert the divide() function to compatible with the above syntax there. Inside there, we’re going to start by returning a new instance of a promise. Promise constructor accepts a function and passes the function inside here. It will have 2 arguments resolve and reject.

Just keep in mind about resolver and reject. These are just 2 references to different functions. So the resolve() is going to be the function inside the then() when you’re using it and the reject() is going to be what’s inside the catch().

For the rejection, if we divide by zero then there’s an error. Let’s use the rejected function to handle the instance of dividing by zero. Use return statement to exit the promise function because we don’t want the code below there to continue running.

function divide(a, b){
	return new Promise(function(resolve, reject) {
    if (b == 0) {
      reject(new Error('You cannot divide by zero'));
      return;
    } 
    resolve(a / b);
  })
};


divide(20, 0).then(function(result){
	document.getElementById("ownPromise").innerHTML = `Division Success: ${result}`;
}).catch(function(error){
  	document.getElementById("ownPromise").innerHTML = `There was an error with division`;
});

(02) Wrapping set timeout function inside a promise

Now let’s define a new function called waitTimer. We’re going be able to wait a certain number of milliseconds and then perform an action. So use the setTimeout function inside the promise.

Wait for 3,000 milliseconds then print to the DOM If something went wrong, then we’ll catch accepting the error.

waitTimer(3000).then(function() {
	document.getElementById('waitTimer').innerHTML = 'Success';
}).catch(function(error) {
	console.log(error);
	document.getElementById('waitTimer').innerHTML = 'Failed' + error;
});

Now let’s define a new function called waitTimer. We’re going be able to wait a certain number of milliseconds and then perform an action. So use the setTimeout function inside the promise and set wait for 3,000 milliseconds.

Print to the DOM If something went wrong and then we’ll catch accepting the error.

function waitTimer(ms) {
	return new Promise(function(resolve, reject) {
  	if (ms < 0) {
	    document.getElementById('waitTimer').innerHTML = 'Cannot wait 0 or less than 0 second';
	    reject(new Error('Cannot wait 0 or less than 0 second'));
      return;
	    
	  }
  	setTimeout(function(){
    	resolve();
    }, ms)
  });
}

waitTimer(-11).then(function() {
	document.getElementById('waitTimer').innerHTML = 'Success';
}).catch(function(error) {
	console.log(error);
	document.getElementById('waitTimer').innerHTML = 'Failed' + error;
});

09. Async/ Await in JavaScript

This is the new way to write asynchronous code. In async-await, Syntaxes are designed to make things easier to read and it built on top of promises. Code look and behave a little more like synchronous code and it’s clear we save a decent amount of code. previous alternatives are callbacks and javascript promises.

Now let’s learn the usage of async-await with easy examples.

 

(1) Async Functions – Start with async keyword and place before the function.

async function asyncFunction() {
  return 'something';
}

Async function always returns a promise. So you can use then() and catch() method to handle success or failure.

async function asyncFunction() {
  return 'something';
}

asyncFunction().then(alert); // something

If you need, you can explicitly return a promise but the result would be the same.

async function asyncFunction() {
   return Promise.resolve('something');

}

asyncFunction().then(alert); // something

(2) Await – Work only inside async functions and make the javascript wait until promise return its result.


10. Thumb rules of Async / Await

(1) Can’t use await in regular function – There would be a syntax error if we try to use await in regular function.

 

function asyncFunction() {
  let promise = Promise.resolve('Something');
  let result = await promise; // Syntax error
}

(2) Await doesn’t work in top-level code – await doesn’t work without an async function.

let response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
let user = await response.json();

// syntax error in top-level code

If you want to use it in top-level code, just wrap it with an anonymous function as below.

(async () => {
  let response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
  let user = await response.json();
})();

11. Error handling in Async / Await?

In case of rejection, we can catch the error using one of the following 2 methods.

1) Using of try..catch
2) Using of .catch() method. (Usually, this is not suited)

1) Using of try..catch – 

async function asyncFunction() {

  try {
    let response = await fetch('ahttps://jsonplaceholder.typicode.com/todos/1');
  } catch(err) {
    alert(err); // TypeError: failed to fetch
  }
}

asyncFunction();

If you want to catch errors in multiple lines, then do as below.

async function asyncFunction() {

  try {
    let response = await fetch('ahttps://jsonplaceholder.typicode.com/todos/1');
    let user = await response.json();

  } catch(err) {
    alert(err); // catches errors both in fetch and response.json
  }
}

asyncFunction();

2) Using of .catch() method –

async function asyncFunction() {
  let response = await fetch('ahttps://jsonplaceholder.typicode.com/todos/1');
}

// asyncFunction() becomes a rejected promise
asyncFunction().catch(alert); // TypeError: failed to fetch // (*)

12. Should we use Promise or Async/ Await?

Before getting into the decision, let finalize what we understood in the promise chain and async-await. So I summarize it with the title of scope, logic and error handling.

Scope – Promise chain itself is asynchronous and the entire wrapper function is asynchronous in async-await.

Logic – Synchronous code can be handled in the same callback in the promise and need to move out of the callback in async-await.

Error Handling –  While promise use then(), catch() and finally() method in a promise chain, async-await use try..catch and finally methods.

 

So the answer is both. let me explain why.

If you are trying to quickly grab the result from the promise and using them in one or 2 different ways, the promise chain would be the best option. Because it reduces the unnecessary async wrapper function when a simple then() method does.

 

If you find yourself writing long, then you have to write complicate waterfalls of then() statements but if you use async-await, it allows you to clean it up one readable asynchronous callback function.

If you have any questions or queries, let us know in the comment section below.

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

Related Articles

Binary Search Algorithm with Example | Data structures

Binary Search Algorithm with Example | Data structuresG, 0 Comments Binary search will take less time than the linear search. The one pre-request of binary search- Array should be sorted. If the data is not sorted in the array, you cannot apply a binary...

React Interview Questions with Answers- (2020)

React Interview Questions with Answers- (2020)G, 0 Comments  Follow me on Medium In this article, we're going to talk all react interview questions with answers and will be useful for the people who get readying or for the interviews or want to improve the...

Javascript Prototype Explained -Advanced JavaScript

Javascript Prototype Explained -Advanced JavaScriptG, 0 Comments  Follow me on Medium In this article, we're going to understand javaScript Prototype from the basic level to advanced concepts. So this would help for the both freshers and experienced...

Stay Up to Date With The Latest News & Updates

Join Our Newsletter

Follow Us