Back to JavaScript
JavaScript
medium
mid

How do you handle errors when multiple requests are sent simultaneously?

Choose by semantics: `Promise.all` rejects on first failure — good for atomic ops. `Promise.allSettled` collects per-item outcomes — good for independent work. Retry transient failures with exponential backoff. Use AbortController to cancel siblings if one critical request fails. Surface partial-success states explicitly in UI.

4 min read·~10 min to think through

Error handling in parallel requests comes down to what should happen when one fails.

Semantics first

SituationHelperFailure behavior
All must succeedPromise.allReject on first; siblings still run but their results are lost.
Partial success acceptablePromise.allSettledAlways resolves with per-item `{status, valuereason}`.
First-to-succeed winsPromise.anyResolves on first success; AggregateError if all fail.
First-to-settle (timeout)Promise.raceResolves/rejects on first settle.

Pattern: independent best-effort

js
const results = await Promise.allSettled(loaders);

return results.map((r, i) =>
  r.status === "fulfilled"
    ? { ok: true, data: r.value }
    : { ok: false, error: r.reason, retry: loaders[i] },
);

Render each card with success or per-card error + retry.

Pattern: atomic with cancel-on-fail

js
const ac = new AbortController();
try {
  const results = await Promise.all([
    fetch("/a", { signal: ac.signal }),
    fetch("/b", { signal: ac.signal }),
    fetch("/c", { signal: ac.signal }),
  ]);
} catch (err) {
  ac.abort();   // cancel siblings still in flight; saves bandwidth
  throw err;
}

Retry transient failures

js
async function retry(fn, { retries = 3, baseMs = 200 } = {}) {
  let last;
  for (let i = 0; i <= retries; i++) {
    try { return await fn(); }
    catch (e) {
      last = e;
      if (!isTransient(e) || i === retries) throw e;
      await new Promise((r) => setTimeout(r, baseMs * 2 ** i + Math.random() * 100));
    }
  }
  throw last;
}

Exponential backoff with jitter; retry only on transient errors (network, 5xx, 429), not 4xx.

Distinguish error kinds

  • Network error — no response (offline, CORS, DNS).
  • HTTP 4xx — client problem; don't retry blindly.
  • HTTP 5xx — server problem; safe to retry.
  • 429 — rate limited; honor Retry-After.
  • AbortError — intentional cancel; treat as not-an-error.

Surfacing partial success

UI patterns:

  • Per-item status (dashboard cards with their own error state).
  • Banner "3 of 5 widgets failed to load".
  • Toast for transient retries with auto-dismiss.
  • Hard error for atomic operations (modal blocking next step).

Don't suppress

Either handle each rejection or let it propagate to a known error boundary. Suppressing with empty catch creates ghosts.

Interview framing

"Pick Promise.all for atomic ops (any failure invalidates the result), Promise.allSettled for independent best-effort work. For atomic ops with side-effecty calls, cancel siblings on failure with AbortController. Retry transient failures (network, 5xx, 429) with exponential backoff + jitter — never 4xx. Surface partial success explicitly: per-card errors, a 'X of Y failed' banner, not a silent half-broken UI. Treat AbortError as not-an-error in your catch."

Follow-up questions

  • What's exponential backoff with jitter?
  • Why is 4xx not retriable?
  • How would you implement a circuit breaker?

Common mistakes

  • Promise.all where allSettled is right (or vice versa).
  • Retrying 4xx errors.
  • Treating AbortError as a real failure.
  • Silent catch.

Performance considerations

  • Retry storms can make outages worse — jitter and a circuit breaker matter at scale.

Edge cases

  • Rate-limited 429 with Retry-After header.
  • User cancels mid-batch.
  • Server returns mixed results in one response (different error shapes).

Real-world examples

  • AWS SDK retry policies, axios interceptors, React Query retry strategy.

Senior engineer discussion

Seniors articulate partial-success UI patterns and retry policy nuances (transient vs permanent, jitter, circuit breakers) rather than just 'wrap in try/catch'.

Related questions