Asynchronous JavaScript — Promises & async/await
Why Async Exists
JavaScript runs on one thread. If a network request blocked it, the entire page would freeze — no clicks, no scrolling. So slow operations (network, timers, file access) run in the background and notify you when done. The event loop coordinates this.
console.log("1");
setTimeout(() => console.log("2"), 0); // even 0ms goes to the queue
console.log("3");
// prints 1, 3, 2 — async callbacks wait for the current code to finishPromises
A Promise is an object representing a future value — pending, then fulfilled or rejected:
fetch("/api/courses") // returns a Promise immediately
.then(response => response.json()) // runs when fulfilled
.then(data => console.log(data))
.catch(error => console.error(error))
.finally(() => hideSpinner());Before Promises, code nested callbacks five levels deep (“callback hell”). Promises flattened it; async/await made it read like normal code.
async / await — the Way You’ll Actually Write It
async function loadCourses() {
try {
const response = await fetch("/api/courses");
if (!response.ok) { // fetch does NOT reject on 404/500!
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
render(data);
} catch (error) {
showError(error.message); // network errors land here too
} finally {
hideSpinner();
}
}awaitpauses this function (not the page) until the Promise settles.awaitonly works insideasyncfunctions (or at module top level).- An
asyncfunction always returns a Promise.
Parallel, Not Sequential
The classic performance bug — awaiting in series when requests are independent:
// SLOW — second request waits for the first (600ms total)
const users = await fetchUsers(); // 300ms
const posts = await fetchPosts(); // 300ms
// FAST — both in flight at once (300ms total)
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts(),
]);Variants: Promise.allSettled() never rejects — you get every result with its status; Promise.race() resolves with the first to finish.
Making Your Own Promises
Mostly you consume Promises, but wrapping callback APIs is worth knowing:
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
await sleep(1000); // pause 1 second
console.log("one second later");