Last 30 Days
No notifications
JavaScript is single-threaded. Long operations (network, files, timers) don't block — they run *asynchronously* and notify you when done. The journey: callbacks → Promises → async/await. Today you write almost everything with async/await and only drop down to raw Promises for combinators like Promise.all.
# Promises & async/await
fetch("/api/users") blocked it, the page would freeze for half a second every request. Instead, the operation is handed to the platform (browser/Node) and a *Promise* is returned immediately. When the result arrives, your callback runs.const wait = ms => new Promise((resolve, reject) => {
setTimeout(resolve, ms);
});const flaky = () => new Promise((resolve, reject) => {
Math.random() < 0.5 ? resolve("ok") : reject(new Error("nope"));
});
.then / .catch / .finallyfetch("/api/users")
.then(res => res.json())
.then(users => console.log(users))
.catch(err => console.error(err))
.finally(() => hideSpinner());async functions ALWAYS return a Promise. await pauses inside the function until the Promise settles.
async function loadUsers() {
try {
const res = await fetch("/api/users");
if (!res.ok) throw new Error(HTTP ${res.status});
const users = await res.json();
return users; // becomes the resolved value of the Promise
} catch (err) {
console.error(err);
return [];
} finally {
hideSpinner();
}
}const users = await loadUsers(); // top-level await OK in modules
// ❌ sequential — total = sum of times
const a = await fetchA();
const b = await fetchB();// ✅ parallel — total = max of times
const [a, b] = await Promise.all([fetchA(), fetchB()]);
| combinator | resolves when… | rejects when… |
Promise.all([a, b]) | ALL fulfilled — value = array of results | ANY rejects |
Promise.allSettled | ALL settled — value = array of {status, value/reason} | never |
Promise.race | FIRST settles (either way) | first settles is reject |
Promise.any | FIRST FULFILLS — value = that result | ALL reject (AggregateError) |
for await … ofasync function* generate() {
yield 1; yield 2; yield 3;
}
for await (const n of generate()) console.log(n);await — if (fetch("/x")) is always truthy (it's a Promise object). The check is meaningless.forEach ignores async — use for…of if you need awaits in order.arr.forEach(async x => await save(x)); // ❌ doesn't wait — fires all at once
for (const x of arr) await save(x); // ✅ sequential
await Promise.all(arr.map(save)); // ✅ parallel, waits for all
then and await — pick one in a function for readability.new Promise constructor are tricky — throws in the executor *do* reject, but throws in async callbacks scheduled inside don't. Just use async functions..catch or wrap awaits in try.fetch and many APIs accept an AbortSignal:
const ac = new AbortController();
setTimeout(() => ac.abort(), 5000);try {
const res = await fetch("/api/slow", { signal: ac.signal });
} catch (e) {
if (e.name === "AbortError") console.log("cancelled");
}