Async/Await: The Simplicity of Writing Asynchronous JavaScript

JP Wallhorn
Syntx — News, Blog & Insights
4 min readNov 27, 2019

Async/Await: The Simplicity of Writing Asynchronous JavaScript

One of the defining qualities of JavaScript is that it is synchronous. It is single-threaded, meaning that at any one time only one block of JavaScript code on a page can be executed, and each line is executed one after the other. Seems simple enough, right? And it is, so long as you aren’t waiting on any data from an external source to be returned.

What happens, though, when you are sending a fetch request and need to use the return value of the request in the next block of code? Left to its own devices, the JavaScript engine will:

  1. Make the fetch request
  2. Return a special JavaScript object called a Promise
  3. Move on to executing the next block of code
  4. Either resolve the Promise or return an error once the request is complete
  5. Continue executing the code synchronously, one block after another

The above sequence describes asynchronous JavaScript, because you can make that fetch request without blocking the thread. Your program can continue to execute code at the same time that the initial fetch request is processing and being completed.

Depending on the code, this sequence might be totally fine. Your program may not need the return value of the fetch request in order to run the subsequent code. Very often though, the code following the request needs that data being fetched in order to execute.

Before we get into asynchronous JavaScript, we are going to discuss what a Promise actually is. This is crucial to understanding asynchronous JavaScript, as it is only made possible by the return of a Promise object. If you already have a solid understanding of Promises, you can move on to the next section.

What Is a Promise?

A Promise is a special JavaScript object, and is called such because it really functions similarly to a promise in real life. Outside of software engineering, a promise refers to a commitment that someone makes to another person. I promise to do something, and you will see soon enough whether I fulfill or fail at that promise.

Promises in JavaScript are the same, but that “something” referred to above is a request. You make your fetch request, and get a Promise object back — a commitment to complete that fetch request, and shortly thereafter you find out whether that Promise is fulfilled or fails.

Here, “fulfilled” means successfully posting, getting, updating or deleting something from a database. “Fails” means you get an error that it was unsuccessful. The part that we want to focus on though is that “shortly thereafter” timeline. If your fetch request is returning a huge amount of data, this period of time will increase accordingly. But never is it instantaneous — not even for a small amount of data. A Promise is always returned, allowing the JavaScript engine to move on to executing the next block of code, but some Promises are resolved more quickly than others.

Asynchronous JavaScript

Asynchronous JavaScript is made possible thanks to Promises. If fetch requests did not return a Promise, the JavaScript engine would have to wait until the fetch request completes in order to move on to the next code block, because of its single threaded nature. For a large request that takes 30 seconds or even a couple of minutes, all code would be blocked from running until its completion — not a great user experience.

At times though, we need to wait. Hopefully the request we are waiting on only takes a couple of seconds or less, but either way, we need it to complete in order for the subsequent code to execute properly. For instance, if the next code block manipulates data returned from the request, executing that code won’t go very well if it’s done before the request completes.

If you have been working with JavaScript, you have likely seen the then/catch syntax used to handle just this problem. Then/catch works totally fine, but let’s be honest, it can get ugly fast. If you are chaining just one command it may be fine, but a series of commands can become messy and very hard to follow or work with.

Async/Await

Lucky for us, we have access to some syntactic sugar that does the exact same thing that a complex series of then/catch commands does with async/await. Instead of the long chain, all we need to do is use the ‘async’ keyword when defining a function, which will then allow you to use the ‘await’ keyword to specify to the engine which code blocks the program does need to wait on a response from before moving on. It’s that simple. You are telling the engine, “Hey, we’re going to have some asynchronous code in this function, but you need to wait for a response wherever you see ‘await’”.

The beauty of this option over then/catch, is that you can write the code exactly as you would otherwise and just add in a few keywords where needed. None of this wrapping entire code blocks into .then() and creating long convoluted chains. Take a look below at the same call written two different ways. The first uses then/catch, the second uses async/await:

const getUsersThen = () => {
return fetch(`localhost:3000/users`, {
method: 'GET',
headers:{
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(res => {
console.log(res)
});
};
const getUsersAwait = async (req, res) => { const users = await fetch(`localhost:3000/users`, { method: 'GET', headers:{ 'Content-Type': 'application/json' } }); const usersJson = users.json(); console.log(usersJson); };

This is an extremely simple function in which we just log the JSON response to the console, but you can already see how much cleaner the async/await version is. This difference is magnified with every subsequent command — with every chained ‘then’ that no longer is needed. With async/await, writing asynchronous JavaScript just became infinitely more simple.

Originally published at https://www.syntx.io on November 27, 2019.

--

--