DEV Community

loading...

Async/Await in JavaScript

Megan Lo
Web Developer πŸ’Ύ | Flatiron Grad πŸ‘©πŸ»β€πŸ’» | Cal '20 Sociology 🐻 | I learn as I write | City Girl At Heart
・5 min read

YOU ARE ALMOST THERE!!!
wink
This will be the last article of the series.

There are 4 parts in this series:

  1. Intro to Asynchronous JS
  2. Promises in JavaScript
  3. More Promises in JavaScript
  4. async, await (this article)

Note: If you are not familiar with the concepts of either callbacks or Promises, I highly recommend you to visit the previous articles first. Promises and callbacks are the precedents of async and await and they are the reasons why async and await exists.

Introduction

As mentioned in the previous article, Promises were introduced in ES2015 and it were meant to solve the callback hell issues. Turns out Promises were not enough. The keywords async and await were introduced in ES2018 to reduce the boilerplate of Promises and to solve the limitation of "don't break the chain" in promises chaining.

Let's see the difference between Promises and async/await!

A promise:

const promise = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve("done!"), 1000);
  })
};
Enter fullscreen mode Exit fullscreen mode

async/await:

const promise = async () => {
  console.log(await promiseAsync());
};
Enter fullscreen mode Exit fullscreen mode

OR

A promise:

const promise = () => {
  return Promise.resolve(1);
};
Enter fullscreen mode Exit fullscreen mode

async/await:

const promise = async () => {
  return 1;
};
Enter fullscreen mode Exit fullscreen mode

By appending the async keyword to any function, the function will return a promise.

Rewriting a promise with async and await

Remember how we printed a list of Harry Potter characters from our previous article? Here's the code as a refresher:

// grab that main element 
const main = document.getElementById("main");

// fetch the third-party API
// I modified this to just a fetch,
// instead of assigning to a variable
fetch("http://hp-api.herokuapp.com/api/characters")
  .then(response => {
      return response.json();
  })
  .then(characters => {
      main.innerHTML = mapCharacters(characters);
  });

const mapCharacters = (characters) => {
  const names = characters.map(character => `<li>${character.name}</li>`);
  return `<ul>${names}</ul>`
}
Enter fullscreen mode Exit fullscreen mode

Let's rewrite this with aysnc/await!

  1. We'll replace .then to await
  2. We should make the function async for them to work!
async function fetchCharacters() {
  // read JSON
  let response = await fetch("http://hp-api.herokuapp.com/api/characters");
  let characters = await response.json();

  main.innerHTML = mapCharacters(characters);
};

fetchCharacters();

const mapCharacters = (characters) => {
  const names = characters.map(character => `<li>${character.name}</li>`);
  return `<ul>${names}</ul>`
}
Enter fullscreen mode Exit fullscreen mode

Now we replace all the .then with await! It's so much cleaner and less boilerplate!!

And for error handlers, all we have to do is (unfortunately, they don't have the await way of doing this to handle error πŸ₯²):

fetchCharacters()
  .catch(error => {
    console.log(error)
  });
Enter fullscreen mode Exit fullscreen mode

A few things to remember

πŸ’― await won't work in the top-level code

When we see await inside a function, there's an async append to the function. However, just a reminder, await won't work in the top-level code. In other words, you can't straight up using the await keyword on a variable:

// this won't work since it's not wrapped
// in an asynchronous function
let response = await fetch("http://hp-api.herokuapp.com/api/characters");
Enter fullscreen mode Exit fullscreen mode

We have to wrap await inside an asynchronous function.

πŸ’― You can catch errors with try...catch

One thing I didn't mention earlier in the previous article (and I apologize) is that Promise.reject(new Error("...")) is equivalent to throw new Error("...").

With try...catch, you can do this:

async function fetchCharacters() {
  try {
    let response = await fetch("http://hp-api.herokuapp.com/api/characters");
    let characters = await response.json();

    main.innerHTML = mapCharacters(characters);
  } catch(err) {
    console.log(err);
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ’― async/await works in Promise.all as well!!

If you came from the previous article, you might still be digesting the concept of Promise.all. But no worries, we are only going to cover very briefly, so you have an idea.

I am going to use the example from the previous article.

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve(console.log(1)), 1000);
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve(console.log(2)), 2000);
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve(console.log(3)), 3000);
});
const p4 = new Promise((resolve, reject) => {
  setTimeout(() => resolve(console.log(4)), 4000);
});
const p5 = new Promise((resolve, reject) => {
  reject(new Error('reject'));
});

// we wrap the promises in Promise.all and then await
let results = await Promise.all([
  p1, 
  p2, 
  p3, 
  p4, 
  p5
])
Enter fullscreen mode Exit fullscreen mode

That's it!!


✨ And there you go!! ✨ Now that not only you learned Promises, you also learned more about async/await! Just remember with async/await, promise.then/catch is rarely used. async/await together provides a great framework to write asynchronous code that is easy to read and write!


Before You Go

Since this is the last article of the series, let's end with a good note by wrapping up what we learned in this series:

  • JavaScript is synchronous and single-threaded. Since JS program is typically event-driven, therefore we care about asynchronous programming.
  • Promises were introduced in ES2015 to solve the callback-hell issue
  • The three states of Promises are pending, resolved, rejected.
  • When a promise object is fulfilled, the result is a value; when a promise object is rejected, the result is an error object.
  • The three consumer handlers in promise: .then, .catch, .finally
  • We can do chain promises with .then
  • We can fetch multiple promises with Promise.all
  • async and await were introduced in ES2018 to reduce the boilerplate of Promises and the limitation of "don't break the chain" of chaining promises.
  • When async/await is used, promise.then/catch is rarely used.

And that's it! I am more than happy if you need to bookmark this series for any refresher in the future!


YAY! You made it to the end! Be proud of yourself! And...
bye gif

If there's any mistake or inaccuracy (with the terminology, etc.) in this series, please feel free to comment below. Like a lot of you who are reading this series, I wrote as I learned.
Hope this series help you build a foundation in asynchronous programming and get rid of the struggle before you read the series!


Last but not least, happy coding!!

tony stark and his computer

Resources

🌟 Modern Asynchronous JavaScript with Async and Await (NodeJS documentation)
🌟 Making asynchronous programming easier with async and await (MDN)
🌟 Async/await
🌟 JavaScript Async/Await Tutorial – Learn Callbacks, Promises, and Async/Await in JS by Making Ice Cream 🍧🍨🍦

Discussion (0)