DEV Community

Kevin Downs
Kevin Downs

Posted on

Promises and Async/ Await

If you have done any work with connecting JavaScript web applications to a back-end database you are probably familiar with the concept of asynchronous code. When making calls to an external API or a query to a database we need to account for the time it takes to send the request and receive a response. If we executed that code synchronously (that is, in order) the code that uses the data we request may run before we even get that data back. I'm sure you can imagine the kinds of headaches dealing with that would cause. In JavaScript, we utilize something called a Promise to handle this exact problem when writing code that needs to be asynchronous.

My own familiarity with promises while learning JavaScript and React was basically limited to the fetch() function. fetch() is a function that returns a promise. Once resolved, we can then handle whatever we need to happen to the returned data in .then() and .catch(e) blocks. This is in essence how a promise works.

The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

As I'm sure you can imagine, there are some drawbacks to using this strategy. If you have asynchronous function calls that rely on data returned from other asynchronous function calls you can end up with quite a mess of nested .then() calls. This can make it difficult to pinpoint errors and debug properly. It can also lead to duplicate error handing code, as asynchronous errors need to be handled in the .then() .catch() chain.

While starting to learn NodeJS and how to query databases and APIs, I came across a different strategy to handle asynchronous code, the async and await keywords. Admittedly, async/ await is built on top of promises so they are not exactly a new or different strategy. Together, these two keywords are some really nice syntactic sugar built on top of the existing promise strategy. They do; however, provide us with some distinct advantages over the traditional promise strategy. This includes being built on top of promises, as we are able to combine traditional .then() and .catch() chains with the syntactic sugar provided by async/ await to make our code as simple, clean, and readable as we can make it.

Using async is as simple as providing the keyword in your function definition. Doing so tells the function to expect the await keyword and to return a promise instead of directly returning the value.

The await keyword is where things really get going. await is a keyword that works only inside of async functions. It tells JavaScript to wait until the promise is resolved and then return the resulting value. While JavaScript is waiting, other code that way be waiting to execute is allowed to do so. Working this way has many advantages the we will talk about soon, but one of them is that we can write asynchronous code that looks more like synchronous code. Lets look at the difference between the same code written using traditional promise syntax and asyc/ await syntax.

// Traditional Promise

function logUsers () {
   fetch('requestEndpoint')
   .then(response => response.json())
   .then(data => {
      console.log(data.users)
   })
   .catch(e -> {
      console.log(e.message)
   })
}

// Async/ Await

async function logUsers () {
   try {
      const data = JSON.parse(await fetch('requestEndpoint'))
      console.log(data.users)
   } catch(err) {
      console.log(err.message)
   }
}

This is a simple example, but I'm sure you can already see some of the advantages I want to point out below.

The code is much simpler and easier to read. There is no chaining or nesting, and it even resembles how we might write synchronous code.

We are also able to handle all of our errors within a try/catch, eliminating the need for chaining .catch() to handle errors with the promise and then handling errors with the data separately. It's important to note here that we could still chain .then and .catch to the return value of our function if we needed to. This example was used to highlight the simplicity you can gain, but there may be cases where a hybrid solution is more appropriate. This flexibility is yet another advantage.

It is not shown in this example, but if we had multiple requests that we needed to make it is easy to see how much simpler await could make that process. Just assign each request to a variable using await as we did above, and then manipulate that data how you need it once it is resolved. No need for complicated fetch calls nested inside .then() calls. Note that awaiting multiple requests together can result in significant wait times for everything to resolve. There are better ways to handle those cases, but they are a bit outside of the scope of this quick overview.

There are many other advantages to using async/ await, and one could probably write forever comparing the intricate difference and strategies between it and traditional promises. If this article has inspired you to want to learn more, feel free to check out the resources I used when writing this article listed below. I'd also love to hear your comments about asyc/ await and experience using them.

If you liked this post, feel free to follow me elsewhere on Twitter, Github, or LinkedIn. Happy Coding!

Documentation

-Promise - MDN
-Master the JavaScript Interview: What is a Promise?
-Async/await
-Making asynchronous programming easier with async and await
-7 Reasons Why JavaScript Async/Await Is Better Than Plain Promises

Top comments (0)