30th August 2020 4 min read

Asynchronous JavaScript - what is it? (Promises, callbacks, async/await)

Omar Benseddik

JavaScript code is executed synchronously. In other words, from top to bottom and one line at a time.

javascript
1function getText() {2  return "Hi 👋, I'm Tinloof";3}4
5let text = getText();6console.log(text);7
8// Output:9// Hi 👋, I'm Tinloof

First, the code will execute the function and know what to return when getText() is called.

Then it assigns the getText() function to the variable text.

Finally, it logs the variable text to the console, and the output is "Hi 👋 , I'm Tinloof".

So far, this works great and we're facing no obstacle.

Now, imagine we have to make a network request to get the text "Hi 👋 , I'm Tinloof" and the user doesn't have a fast Internet connection.

jsx
1// Assume getTextFromServer is making a network request to get data2
3let text = getTextFromServer();4
5// 🕰 Wait until we receive the text from the server6// 🍦 Meanwhile the page is frozen and the user can't interact with it7
8console.log(text);9
10// Output:11// Hi 👋, I'm Tinloof

The code works, but while we wait for the text from the server, our page freezes.

One appraoch to solve this problem is called "callbacks".

Callback functions

javascript
1getTextFromServer((error, text) => {2  if (error) {3    console.log("Error getting the text:", error);4  } else {5    console.log(text);6  }7});8
9// Output (if we successfully get the text from the server)10// Hi 👋 , I'm Tinloof11
12// Output (if we are not successfully getting the text from the server)13// Error getting the text: some error from the server.

Instead of waiting for getTextFromServer() to finish, we let it run in the background and pass to it a function, called "callback function" or "callback", to handle the result of the call once it's done.

To handle the scenario where the request fails or the one where it succeeds, our callback function takes 2 parameters:

  1. An error, which is empty if the request is successful
  2. A result (in our case it's the text "Hi 👋 , I'm Tinloof")
Notice: If you're not familiar with arrow functions, here's a link to the MDN documentation.

We just wrote our first asynchronous code!

Guess what? We just learned one approach of writing asynchronous code in JavaScript.

In the example above, while our code is looking to get the text from the server, the rest of our code would still run.

Another approach to asynchronous code is called Promises.

Promise

javascript
1let promise = getTextFromServer();2
3promise4  .then((text) => {5    console.log(text);6  })7  .catch((error) => {8    console.log("Error getting the text:", error);9  });10
11// Output (if we successfully get the text from the server)12// Hi 👋 , I'm Tinloof13
14// Output (if we are not successfully getting the text from the server)15// Error getting the text: some error from the server.

Instead of accepting a callback, getTextFromServer() returns a Promise object.

A Promise is an object that gives us the result of the success of an asynchronous operation or the result of its failure (it either resolves or rejects).

It does that by providing a then() function to handle success and catch() to handle errors.

JavaScript has a syntatic sugar (jargon for "more beautiful syntax") for Promises, let's check it out.

async/await

javascript
1try {2  let text = await getTextFromServer();3  console.log(text);4} catch (error) {5  console.log("Error getting the text:", error);6}7
8// Output (if we successfully get the text from the server)9// Hi 👋 , I'm Tinloof10
11// Output (if we are not successfully getting the text from the server)12// Error getting the text: some error from the server.

Instead of using the Promise syntax, which can be confusing at times, we simply await getTextFromServer() using the await keyword.

To handle the error and success scenarios, we surround our code in a try/catch block.

If the request is successful, the try block will be executed to its end and the text will be printed.

If the request fails, we'll jump directly from the await line to the catch block and output the error.

Using "await" in a function

If we want to use the await syntax in a function, the function has to be declared with the async keyword.

javascript
1async function getText() {2  try {3    let text = await getTextFromServer();4    console.log(text);5  } catch (error) {6    console.log("Error getting the text:", error);7  }8}9
10console.log(getText);11
12// Output (if we successfully get the text from the server)13// Hi 👋 , I'm Tinloof14
15// Output (if we are not successfully getting the text from the server)16// Error getting the text: some error from the server.

Conclusion

We now know what Asynchronous JavaScript is, and learned how to write it with 3 approaches:

  • Callback functions
  • Promises
  • async...await (which is just a prettier syntax of Promises)

Believe it or not, if you made it so far while understanding everything, you can build most features that require asynchronous code.

Though, we strongly advise you to dig deeper in the topic. Here are a few resources:

What the heck is the event loop anyway? by Philip Roberts

Asynchronous programming by Axel Rauschmayer

Rethinking Asynchronous JavaScript by Kyle Simpson on Frontend Masters. You can also read the book YDKJS for free here

Async + Await in JavaScript by Wes Bos

Async-Await cheatsheet from CodeCademy