How to run fetch in a loop?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Running fetch in a loop is common when you are loading paginated data, calling multiple endpoints, or processing a batch of IDs. The key design question is whether the requests must happen one after another or whether they can run in parallel, because the control flow and performance characteristics are very different.
Sequential fetch with for...of
If each request depends on the previous one, or if you want very simple control flow, use a normal loop with await.
This is often the right answer for pagination, retry-heavy workflows, or any case where each step depends on the last response. It is also easier to debug because the order is deterministic.
Parallel Requests with Promise.all
If the requests are independent, you can launch them all at once and wait for all of them to finish.
This usually reduces total time to roughly the slowest single request rather than the sum of all request times. The tradeoff is that one failure rejects the whole Promise.all result unless you deliberately handle each request separately.
Avoid forEach with async
One of the most common mistakes is writing urls.forEach(async (url) => ...) and expecting the outer code to wait. forEach does not understand await the way a for...of loop does.
Bad pattern:
Preferred pattern:
If you want concurrency, use map plus Promise.all, not forEach.
Concurrency-Limited Loops
Launching hundreds of requests at once can trigger rate limits or overwhelm the server. A worker-pool pattern lets you keep concurrency under control.
This is a good middle ground when requests are independent but the API cannot handle unlimited parallel traffic.
Looping Through Pagination
A common real-world case is fetching pages until the API says there are no more.
This pattern is clearer than recursion for most API clients and keeps pagination state explicit.
Timeouts, Retries, and Cancellation
Network loops need failure policy, not just syntax. A timeout wrapper prevents a single slow request from hanging the whole batch.
For retries, wrap the request in a helper:
These helpers make loops much more reliable when networks are imperfect.
Common Pitfalls
The most common mistake is using forEach with async callbacks and expecting sequential await behavior. Another is firing too many requests in parallel and hitting throttling or rate limits. Developers also forget to check response.ok, which means error pages are treated as normal responses. Finally, network code without timeouts or retries tends to fail in production even when it looked fine during local testing.
Summary
- Use
for...ofwithawaitfor sequential request loops. - Use
Promise.allwhen requests are independent and can run in parallel. - Limit concurrency for large batches or rate-limited APIs.
- Use explicit pagination loops for multi-page APIs.
- Add timeout, retry, and response-status handling to make the loop reliable.

