Asynchronous JavaScript and Promises
Before diving into async/await
, you must first have a strong grasp of asynchronous
programming. If you're unfamiliar with the concept, you can check out an article I wrote about it here:
...furthermore, since async/await
deals with Promises, you should also have
a solid understanding on when, how, and why to use Promises. This article serves as a continuation to the previous
article I wrote, which can be found here.
With that out the way, we can finally dive into async/await
.
What is async/await?
The async/await
keywords were first introduced in ES7 as a way to enable
asynchronous, promise-based behavior. They serve as an alternative to the .then()/.catch()
methods used in ES6, all while providing a cleaner and more concise syntax that resembles synchronous code.
async keyword
Take a look at this arrow function:
const sayHi = () => {
return "hi!";
};
sayHi();
// hi
...to no surprise, hi
is logged to the console. What if we converted this regular function
to an asynchronous function? This can be done by adding the async
keyword, which
is used to declare asynchronous functions...
const sayHi = async () => {
return "hi!";
};
sayHi();
// Promise {fulfilled: 'hi'}
...since sayHi
is now an asynchronous function, it returns a Promise, in this case, a fulfilled Promise
that holds the value of hi
. Consuming this value is nothing new and can
easily be achieved with a simple .then()
callback...
const sayHi = async () => {
return "hi";
};
sayHi().then((greeting) => {
console.log(greeting);
});
// hi
...this works but you would only end up using .then()
and
.catch()
again, which defeats the whole purpose of async
.
Instead, let's combine it with the await
keyword.
await keyword
Unlike the async
keyword which is used only once outside a function, the
await
keyword can be used multiple times inside asynchronous functions.
await
will pause further code execution until the Promise settles and returns a result.
This is extremely useful when doing things like making API requests, where there's a chance that the data won't
be returned immediately. As an example, I'll use the setTimeout()
method, which will serve as a mock API request:
const sayHi = async () => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("hi");
}, 3000);
});
let greeting = await promise;
console.log(greeting);
};
sayHi();
// hi
...here, an asynchronous function sayHi
is created. Inside the function is a
promise that resolves with a value of hi
after 3 seconds. We can pretend this
is a slow API request. Since the data inside promise
takes a while to arrive, we must wait until the Promise settles before continuing code execution.
In order to do this, we use the await
keyword. Do note: JavaScript execution
will only pause inside said asynchronous function; other tasks and events will continue to be executed, making
it super efficient and resourceful.
Handling errors
It'd be nice if programs alway ran without any errors, but this isn't the case. Sometimes you'll encounter errors, so it's
important to know how to deal with them. Luckily, errors can easily be caught by using the
try...catch
statement.
The try...catch
statement contains two blocks: try
and catch
. Anything inside the try
block will
be executed first, and if an error occurs, the catch
block will fire. Here's a more
realistic example using the Fetch API to grab data from a nonexistent endpoint:
const someData = async () => {
try {
const response = await fetch("https://some-endpoint");
const data = await response.json();
console.log(data);
} catch (err) {
console.log(err);
}
};
someData();
// TypeError: Failed to fetch
...when someData
is invoked, JavaScript will first attempt to access the
endpoint, return the data, and log it; if that fails, an error will be logged to the console. Since the
used resource is fake, the try
block will fail and
fire the catch
block.
Although async/await
is used for asynchronous operations, the actual syntax
looks synchronous, which makes it super clean and concise. Additionally, it also helps get rid of promise chaining altogether.
Despite this, you should still know how to use both methods in case you ever need to use one or the other.