An Introduction to Callbacks, Promises, and Asyc/await in JavaScript

Ajmal Jalal
Sep 3, 2018 · 6 min read
“yellow and blue data code displayed on screen” by Markus Spiske on Unsplash

What does asynchronous programing mean?

In very basic words; when we talk to a database, downloading things or reading some files, it does not happen very fast. It takes some time, it happens asynchronously. The value we expect from these operations is not returned right away. The following code explains both async (takes time) and sync (returns the value right away) operations:

The code above adds two numbers and returns the result right away.

In the code above, photo is undefined. Because by the time we run the downloadPhoto function, photo is not yet returned from the getPhoto function.

In languages like ruby, phyton etc… the donwloadPhoto function would not run until the getPhoto function is done returning the photo. Therefore, it is not a problem, but JavaScript is different. The program will not wait for the getPhoto function, it will go to the next line of code and execute it, because the photo is still undefined it will cause the program to crash. So, there should be a solution, a way to make the program handle this situation properly.

Callbacks come to rescue. Callback is a function that we pass to another function (to the function that takes time to return a value), here to the getPhoto function. getPhoto will take some time, return the value and pass it as an argument to the callback function. Then, inside the callback function we can operate on the returned value. With a callback function, the code above will look something like this:

Here the flow of the program is as bellow:

  1. It declares the downloadPhoto function first
  2. It runs the getPhoto function and passes the downloadPhoto function to it, and waits for the photo from the URL
  3. It prints the “I am not blocked; I am running”
  4. It gets the photo from the URL and passes it to the downloadPhoto function, or it returns an error.
  5. The downloadPhoto function runs and prints “here is the photo”. Or, if there was an error it prints “there was an error getting the photo”.

It looks very simple so far, but it gets complicated when we have several nested callback functions. There are many occasions where we need to nest callback functions inside each other. For example, we need to pass more callbacks if we want to find a user in a database and save the photo under the user’s record:

In the code above, the program first attempts to get the photo, once the photo is returned, the program passes it to the callback function. Inside the body of the callback function another function is called to find a user, and to save the photo under the user record, another callback is called with the returned “user” value. Finally save function is called to save the user that was updated with a photo, and inside it, another callback function is called to return the saved user.

It is obvious now that the program is much complicated, less readable, hard to maintain, and even hard to explain. If we needed to pass some more callbacks, which usually happens, we would have much complicated code. This is called callback hell or The Pyramid of Doom, as it makes the code look like a pyramid.

Promises:

JavaScript has a solution to avoid these nested callbacks. It is called promises, which are less complicated, easy to read and more maintainable. The MDN web docs states: “Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.” It looks like promises are also built over callbacks, but they are cleaner and less prone to errors.

It is easier to understand promises by converting our example from nested callbacks into promises’ chains. You will understand what I am talking about bellow:

Consider the above promises’ chain as a syntactic sugar, do not worry now about what is happening behind the scene. Take it as an easy way to make asynchronous operations. Once you have the basic idea you can then dive deeper into the concept.

Although our code is now less complicated, more readable, and maintainable you may say that it is still a chain of functions, and callbacks are used inside .then() intensively. So, it is still not an ideal situation. You are right, we want to have more readable and maintainable code. Fortunately, recent upgrades to JavaScript gives us a more comprehensive solution.

async/await:

Do not be scared by these words, it is not scary as it looks. async/await is nothing but the same promises we covered above. It is just a more developer friendly syntactic sugar. It allows us to write promise-based code as it is synchronous, but without blocking the main thread. We do not need to chain functions and pass callbacks anymore.

Let’s see what happens to the code above if we re-write it the async/await way:

The code above is very straight forward. The only parts that need explanation is the async and await keywords. When we want to return a promise from a function we need to put the async keyword before the function definition. Every operation inside the async function that needs time to return a value can then be prefixed with the await keyword. Every operation that takes time to return a value, in simple terms, is a promise. By using the await keyword we await the promise, in other terms, we pause the function in a none-blocking way until the promise settles. We also put our code inside a try-catch block. This way the program tries to run the first part of the code, and if an error is thrown it will catch the error in the second(catch) block.

Our code is now very simple, easy to read and maintain. Although we can see the value of async/await in our simple code but the real value of async/await reveals better in a more complex program.

Conclusion:

To handle asynchronous operations, we use callbacks. But callbacks cause complexity. Promises help us get rid of the complexity to some extent. But promises do not provide an ideal situation as well. async/await, on the other hand, gives us more clean, readable, and maintainable code.

If you want to understand what is going on behind the scene and dive deeper into the concept of asynchronous programing in JavaScript, I recommend reading the MDN docs and some other articles and tutorials that are available online.

Some useful resources are listed below:

  1. Promises
  2. Async
  3. Callbacks

If you found value in this piece please give it some claps. It will let me know that I am doing well.
thanks!

Ajmal Jalal

Written by

Full stack web developer | MERN Stack |

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade