Fetch And Retry
Implement a retry function that handles transient failures by retrying async operations with optional delays.
JavaScript · Promises · Async/Await — Pratik Rai ·
Implement two async functions A and B, then return their sum using both sequential and parallel Promise execution, observing the time difference.
Running three requests one after another takes as long as all three combined. Running them together takes as long as the slowest one. Choosing wrong is one of the most common performance mistakes in async JavaScript — and one of the easiest to fix, because the two versions differ by about ten characters.
Three requests, 200ms each:
JSfile.javascript1// Sequential — 600ms 2const user = await fetchUser(id); 3const posts = await fetchPosts(id); 4const comments = await fetchComments(id); 5 6// Parallel — 200ms 7const [user, posts, comments] = await Promise.all([ 8 fetchUser(id), 9 fetchPosts(id), 10 fetchComments(id), 11]);
The sequential version waits for each response before starting the next request. Nothing about these three operations requires that — they are independent.
Await in a chain only when a later call needs an earlier result. Everything else should start together.
JSfile.javascript1// Genuinely sequential — the second call needs the first's output 2const user = await fetchUser(id); 3const org = await fetchOrg(user.orgId); 4 5// Mixed — get the user first, then fan out 6const user = await fetchUser(id); 7const [org, posts] = await Promise.all([ 8 fetchOrg(user.orgId), 9 fetchPosts(user.id), 10]);
The dependency graph decides the shape. Draw it and the code writes itself.
await inside a loopThis is where sequential execution sneaks in unnoticed:
JSfile.javascript1// Sequential — n × latency 2const results = []; 3for (const id of ids) { 4 results.push(await fetchItem(id)); 5} 6 7// Parallel — one round trip 8const results = await Promise.all(ids.map((id) => fetchItem(id)));
For 50 items at 100ms each, that is five seconds versus one hundred milliseconds. for...of with await is the single most common cause of an inexplicably slow page.
Note that forEach does not work here — it ignores the promises its callback returns, so execution continues before anything resolves:
JSfile.javascript1ids.forEach(async (id) => { await fetchItem(id); }); // does not wait for anything
The detail that makes everything above make sense. A promise begins executing the moment it is constructed. await only decides when you pause for the result.
JSfile.javascript1const a = fetchA(); // already in flight 2const b = fetchB(); // also in flight — both running concurrently 3const resultA = await a; 4const resultB = await b; // likely already resolved
This runs in parallel despite two sequential await lines, because both promises were created before either was awaited. It is why Promise.all is not doing anything magical — the array literal starts every promise, and all just waits for them.
The corollary is a real trap: creating promises early and awaiting them much later means a rejection may occur before any handler is attached, producing an unhandled-rejection warning. Create and await in the same logical block.
Rate limits. Firing 500 requests simultaneously will hit an API's limit, get you a wall of 429s, and possibly a temporary ban. Full parallelism is only free when the count is bounded and small.
Resource exhaustion. Each in-flight request holds a socket and memory. Browsers cap concurrent connections per host at around six anyway, so beyond that you gain queueing, not speed. On a server, unbounded concurrency is a route to running out of file descriptors.
Ordered side effects. If each operation depends on the previous one having completed — writing sequential log entries, applying migrations — parallel is simply incorrect.
For a large list, the answer is neither fully sequential nor fully parallel but a concurrency pool:
JSfile.javascript1async function pool(items, limit, worker) { 2 const results = new Array(items.length); 3 let cursor = 0; 4 5 const runners = Array.from({ length: Math.min(limit, items.length) }, async () => { 6 while (cursor < items.length) { 7 const index = cursor++; 8 results[index] = await worker(items[index], index); 9 } 10 }); 11 12 await Promise.all(runners); 13 return results; 14} 15 16const data = await pool(ids, 5, (id) => fetchItem(id));
A shared cursor keeps exactly limit operations in flight — as one finishes, that runner picks up the next index. This is what you want for anything above roughly ten items.
With Promise.all, one rejection rejects the whole thing and you lose the successful results. If partial success is acceptable, use allSettled instead. And remember that neither cancels the operations still running — for that you need AbortController.
await only where a genuine data dependency exists.await inside a for loop serialises everything — usually a bug.forEach ignores async callbacks entirely.await.Promise.all discards successes on first failure — allSettled when partial results are useful.Goal: Understand the difference between sequential and parallel Promise execution and how to use Promise.all to avoid async waterfalls.
Continue learning with these related challenges
Implement a retry function that handles transient failures by retrying async operations with optional delays.
JavaScript · Promises · Async/Await — Pratik Rai ·
Implement your own Promise.all polyfill to understand how concurrent Promise handling works under the hood.
JavaScript · Promises · Async/Await — Pratik Rai ·
Implement the three Promise utility variants: allSettled (never rejects), race (first to settle wins), and any (first to resolve wins).
JavaScript · Promises · Async — Pratik Rai ·
Implement a retry function that handles transient failures by retrying async operations with optional delays.
JavaScript · Promises · Async/Await
Pratik Rai ·
Implement your own Promise.all polyfill to understand how concurrent Promise handling works under the hood.
JavaScript · Promises · Async/Await
Pratik Rai ·
Implement the three Promise utility variants: allSettled (never rejects), race (first to settle wins), and any (first to resolve wins).
JavaScript · Promises · Async
Pratik Rai ·