Async JavaScript and fetch
Promises, async/await, real fetch usage with error handling, cancellation, and running tasks concurrently.
Promises in one page
A promise is a placeholder for a value that is not ready yet. It settles once β either fulfilled with a value, or rejected with a reason.
p
.then(value => transform(value))
.catch(err => console.error(err))
.finally(() => hideSpinner());
// same thing, flatter
async function run() {
try {
const value = await p;
return transform(value);
} catch (err) {
console.error(err);
} finally {
hideSpinner();
}
}π‘
await only works inside an async function (or at the top level of an ES module). Top-level await is supported in modules but blocks importing modules until it settles.fetch, properly
async function getJson(url) {
const res = await fetch(url, {
headers: { Accept: 'application/json' },
credentials: 'same-origin'
});
if (!res.ok) { // fetch does NOT throw on 404/500
throw new Error('HTTP ' + res.status);
}
return res.json();
}β οΈ
The single most common fetch bug: it rejects only on network failure, so a 500 response resolves happily. Always check
res.ok before reading the body.await fetch('/api/item', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'new' })
});
// timeouts with AbortController
const ac = new AbortController();
setTimeout(() => ac.abort(), 8000);
const res = await fetch(url, { signal: ac.signal });Concurrency patterns
// sequential - each waits for the previous
for (const id of ids) results.push(await get(id));
// parallel - start all, wait for all
const results = await Promise.all(ids.map(get));
// tolerate individual failures
const settled = await Promise.allSettled(ids.map(get));
const ok = settled.filter(r => r.status === 'fulfilled').map(r => r.value);
// race: first to settle
const fastest = await Promise.any([fetch(a), fetch(b)]);β οΈ
Promise.all rejects entirely on the first failure. Use allSettled when partial success is acceptable β dashboards, feeds, bulk imports.await inside loops
forEach cannot await properly β the callbacks start, but nothing waits for them. Use forβ¦of for sequential work, or map into Promise.all for parallel.
// wrong: fire and forget
items.forEach(async i => { await save(i); });
// right (sequential)
for (const i of items) await save(i);
// right (parallel)
await Promise.all(items.map(save));FAQ
How do I retry a failed request?
Wrap the call and retry with backoff, but only for idempotent requests and transient statuses β retrying a POST can create duplicates.
Does await block the UI?
No. It suspends only the async function; the event loop keeps handling input and rendering.
Related
Last refreshed 2026-09-17.