HuntExams Academy logo
JavaScriptIntermediate#async#performance

How do you run async calls in parallel with async/await?

Answer

Start all the promises first, then await them together with Promise.all. Awaiting inside a loop serialises the requests and multiplies latency.

Example

// slow (sequential)
for (const id of ids) await fetchUser(id);
// fast (parallel)
await Promise.all(ids.map(fetchUser));

Detailed explanation

To run independent asynchronous calls concurrently with async/await, start every operation before waiting for the combined result. A typical pattern is `const users = await Promise.all(ids.map(id => fetchUser(id)));`. The map callback invokes fetchUser for each ID immediately and collects its returned promises. Promise.all creates one promise representing the group, and await pauses the surrounding async function until that group succeeds or one input rejects. This is useful when no request needs another request’s result.

In contrast, `for (const id of ids) { await fetchUser(id); }` starts the next request only after the previous one finishes. Its total waiting time is roughly the sum of individual request durations. With concurrent requests, successful completion time is closer to the slowest request’s duration, subject to connection limits, server capacity, and other overhead. Sequential execution is still correct when operations depend on earlier results or must happen in a specific order; it is not inherently a mistake.

Under the hood, async/await does not create JavaScript threads. Network I/O can overlap while the runtime handles completion events and schedules promise continuations as microtasks. Promise.all coordinates results; it does not start the operations itself. Those operations usually start when their functions are called. It also preserves input order in the resulting array, regardless of completion order, so `users[i]` corresponds to `ids[i]`. However, putting CPU-heavy synchronous work inside async functions does not make that work execute in parallel on the JavaScript thread.

Handle failure with try/catch around the combined await. Promise.all rejects as soon as an input rejection is observed, but it does not cancel requests already in progress. If every outcome matters, use Promise.allSettled and inspect each status. For very large lists, use a concurrency-limited worker pool rather than launching everything at once. Interviewers look for an understanding of independent versus dependent work, result ordering, rejection behaviour, and resource limits—not just knowledge of the Promise.all syntax.

Key points to remember

  • Use `await Promise.all(ids.map(id => fetchUser(id)))` for independent requests.
  • Calling the async functions starts their operations; Promise.all aggregates their promises.
  • Awaiting each request inside a for loop makes those requests sequential.
  • Promise.all preserves input order and rejects on an input rejection without cancelling remaining work.
  • Limit concurrency for large workloads; overlapping I/O is not parallel JavaScript execution.

Common mistakes candidates make

  • Awaiting each operation before building the promise array, accidentally serialising independent work.
  • Using `await ids.forEach(async id => ...)`, even though forEach does not collect or await callback promises.
  • Assuming Promise.all cancels other requests after a failure or safely supports unlimited concurrency.

Likely follow-up questions

When should you use Promise.allSettled instead of Promise.all?

Use Promise.allSettled when you need the outcome of every operation, even if some fail. It waits for all inputs and returns objects containing a status plus either a value or a reason.

How do you limit the number of simultaneous requests?

Use a worker pool or concurrency limiter that starts a new request when an active one finishes. This keeps at most a chosen number of requests in flight without unnecessarily waiting for an entire batch.

Can you cancel remaining fetch requests when one fails?

Promise.all does not cancel them automatically. Pass an AbortController signal to the fetch calls and call abort when you decide to stop the remaining requests; this does not undo work already performed by the server.

Interview tip: Show the sequential and Promise.all versions, then explain independence, ordered results, and failure handling.

Related Interview Questions

1
JavaScriptBeginner#async

What is callback hell and how do you avoid it?

Open
2
JavaScriptIntermediate#dom#events

What is event bubbling, capturing and delegation?

Open
3
JavaScriptIntermediate#dom#events

Difference between stopPropagation and preventDefault?

Open