Asynchronous JavaScript: Promises and async/await
Introduction
Many things in JavaScript take time: network requests, timers, reading files. Instead of blocking the program, JavaScript uses promises to represent a result that will arrive later. This tutorial covers promises, the async/await syntax that makes them easier to read, and running tasks in parallel.
Prerequisites
- JavaScript functions and arrow functions
- Basic understanding of callbacks
Step-by-Step Guide
Step 1: Understand and create a promise
A promise is in one of three states: pending, fulfilled, or rejected. Wrap a callback-based API in a promise by calling resolve when the work completes.
const wait = (ms) =>
new Promise((resolve) => setTimeout(resolve, ms));
wait(1000).then(() => console.log("One second passed"));Step 2: Chain then and catch
then handles a successful result and catch handles any error in the chain. fetch returns a promise for an HTTP response.
fetch("https://api.example.com/users/1")
.then((response) => response.json())
.then((user) => console.log(user.name))
.catch((error) => console.error(error));Step 3: Write it with async/await
An async function always returns a promise. Inside it, await pauses until a promise settles, so asynchronous code reads top to bottom like synchronous code.
async function loadUser(id) {
const response = await fetch("https://api.example.com/users/" + id);
const user = await response.json();
return user;
}Step 4: Handle errors with try/catch
fetch only rejects on network failures, not on HTTP error statuses like 404. Check response.ok yourself and throw if it's false.
async function loadUserSafely(id) {
try {
const response = await fetch("https://api.example.com/users/" + id);
if (!response.ok) {
throw new Error("Request failed: " + response.status);
}
return await response.json();
} catch (error) {
console.error("Could not load user:", error.message);
}
}Step 5: Run independent tasks in parallel
Promise.all starts several promises at once and waits for all of them, which is faster than awaiting them one after another. This example runs inside an async function.
const [user, posts] = await Promise.all([
fetch("/api/user").then((r) => r.json()),
fetch("/api/posts").then((r) => r.json()),
]);FAQs
- Why doesn't fetch throw an error on a 404?
- fetch only rejects when the request itself fails, such as a network error. HTTP error statuses still resolve, so you need to check response.ok or response.status.
- Is async/await different from promises?
- No. async/await is syntax built on top of promises. It makes the same asynchronous code easier to read and to wrap in try/catch.
Summary
Promises represent future results, and async/await makes them readable. Always handle errors, check response.ok when using fetch, and use Promise.all to run independent work in parallel.
Comments
Log in or create a free account to join the discussion.