Some things take time: network requests, timers, reading files. JavaScript
is single-threaded, so instead of waiting (and freezing the page), it uses
callbacks scheduled for later — organized through promises and
async/await.
Feel the event loop
Run this and watch the order — predict it before pressing play:
The event loop model: synchronous code → microtasks (promises) → macrotasks (timers). Interviewers love this ordering.
Promises
A promise represents a future value. Here's a fake API — same shape as a
real fetch() call, just delayed instead of networked:
async / await
async functions let you consume promises with await — asynchronous code
that reads top-to-bottom:
async function loadUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json(); // also a promise — bodies stream in
}Errors are caught with regular try/catch:
try {
const user = await loadUser(7);
render(user);
} catch (err) {
showError(err.message);
}Parallel vs sequential
Two awaits in a row wait for one THEN the other. When calls are independent, start them together and await both:
// Sequential: 300ms + 300ms = 600ms
const a = await fetch("/api/a").then(r => r.json());
const b = await fetch("/api/b").then(r => r.json());
// Parallel: max(300, 300) = 300ms
const [a2, b2] = await Promise.all([
fetch("/api/a").then(r => r.json()),
fetch("/api/b").then(r => r.json()),
]);Promise combinators:
| Combinator | Resolves when |
|---|---|
Promise.all |
all succeed — rejects fast on FIRST failure |
Promise.allSettled |
all settle — never rejects, inspect each status |
Promise.race |
first to settle wins (timeouts) |
Promise.any |
first success |
Production patterns
Retry with exponential backoff, and cancel slow requests:
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const r = await fetch(url);
if (r.ok) return r.json();
} catch {}
await new Promise(res => setTimeout(res, 1000 * 2 ** i)); // 1s, 2s, 4s
}
throw new Error(`Failed after ${retries} attempts`);
}
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000); // hard timeout
await fetch(url, { signal: controller.signal });Knowledge Check
4 questions — test your understanding
Output order: log('1'); setTimeout(() => log('2'), 0); Promise.resolve().then(() => log('3')); log('4');
Promise.all rejects when...
What does await actually do?
Why is response.json() awaited?
Next: Modern JavaScript (ES6+)