Async/Await Explained
Writing asynchronous code that reads like synchronous code, built on top of promises.
2 min read
async/await, added in ES2017, is syntax built directly on top of promises — it doesn't replace them, it gives you a way to write promise-based code that reads top to bottom instead of chained through .then().
The async keyword
Marking a function async makes it always return a promise, even if you return a plain value inside it:
async function getGreeting() {
return "Hello!";
}
getGreeting().then(value => console.log(value)); // "Hello!"getGreeting() doesn't return the string directly — it returns a promise that resolves to that string, which is what unlocks the next keyword.
The await keyword
Inside an async function, await pauses execution until a promise settles, then gives you its resolved value directly — no .then() needed:
async function loadUser(id) {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}Compare that to the equivalent .then() chain:
function loadUser(id) {
return fetch(`/api/users/${id}`)
.then(response => response.json())
.then(user => user);
}Both do the same thing, but the async/await version reads like ordinary sequential code — no nested callbacks, no chained .then() calls to follow. await can only be used inside a function marked async (or, in modern environments, at the top level of a module).
Error handling with try/catch
Because await unwraps a promise's value directly, a rejected promise becomes a thrown error you catch with ordinary try/catch — the same mechanism covered later in this course for synchronous errors:
async function loadUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error("Request failed");
return await response.json();
} catch (error) {
console.error("Failed to load user:", error);
return null;
}
}await doesn't block the whole program
It's easy to assume await "blocks" everything the way a synchronous wait would. It doesn't — it only pauses the execution of the async function it's inside. The rest of the JavaScript engine (other event handlers, other code) keeps running normally; only the code after that await, within that same function, waits for the promise to settle.
Awaiting multiple promises
Awaiting one at a time runs them sequentially, even when they don't depend on each other:
const userA = await fetchUser(1); // waits, then...
const userB = await fetchUser(2); // ...starts thisTo run them concurrently, start both promises first and await Promise.all:
const [userA, userB] = await Promise.all([fetchUser(1), fetchUser(2)]);async/await and .then() chains are two syntaxes for the exact same underlying mechanism. The next lesson looks directly at when each one is the better choice.