Making multiple HTTP requests asynchronously
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Making several HTTP requests asynchronously lets your program wait for network responses in parallel instead of one by one. The core idea is simple: start the requests first, then await their results together, while still handling failures and rate limits deliberately.
Run Requests Concurrently with Promise.all
In modern JavaScript, the usual tool is Promise.all. It takes an array of promises and resolves when all of them succeed.
This is concurrent, not sequential. All three requests start before the code waits for the results. That usually makes total runtime closer to the slowest request instead of the sum of all request times.
Know the Difference Between Sequential and Concurrent
This loop is sequential:
Each request waits for the previous one to finish. That is sometimes correct, but not if the requests are independent and you want speed.
The concurrent version starts them together:
That change in structure is what makes the code asynchronous in a useful way.
Handle Partial Failure with Promise.allSettled
Promise.all fails fast: if one request rejects, the whole combined promise rejects. Sometimes that is what you want. In other cases, you want successful results even if one request fails.
Use Promise.allSettled for that:
This is useful for dashboards, batch fetches, or aggregation jobs where one bad endpoint should not discard every other successful response.
Limit Concurrency When the Request Count Is Large
Asynchronous does not mean "fire ten thousand requests at once." Too much concurrency can hit API rate limits, exhaust sockets, or overwhelm the remote service.
A simple batching approach is often enough:
This keeps concurrency controlled without giving up the performance benefits of overlapping I/O.
Common Pitfalls
The biggest mistake is writing await inside a loop and assuming the code is still concurrent. If each iteration waits immediately, the requests are sequential.
Another issue is ignoring error handling. Network calls fail in normal operation, so decide whether one failure should cancel the whole batch or only mark one item as failed.
Rate limits are also easy to underestimate. Even if your code works locally, production traffic can overwhelm an API if concurrency is unbounded.
Finally, do not forget response parsing. fetch() resolves for HTTP error statuses too, so check response.ok when the status code matters instead of assuming every resolved promise means success.
Summary
- Start independent HTTP requests first, then await them together.
- Use
Promise.allwhen every request must succeed. - Use
Promise.allSettledwhen partial success is acceptable. - Limit concurrency for large batches to avoid rate limits and resource pressure.
- Remember that
fetch()resolving does not automatically mean the HTTP status was successful.

