Stop Stacking Awaits: Two-Stage Parallelism with Promise.all
Frontend performance is often bottlenecked by waterfall requests that could run in parallel. Replacing serial awaits with Promise.all is a low-effort, high-impact change, and the two-stage pattern eliminates the hidden serial cost of response parsing that many developers overlook.
A naive async/await chain fires one request, waits for it, then fires the next — total time is the sum of every round-trip. Promise.all dispatches all independent fetches at once, so the wall-clock cost drops to the slowest single request. The technique goes further: fetch returns a Response, and .json() is itself a Promise, so a second Promise.all over the response array parallelizes the parsing stage too. The result is a two-stage concurrent pipeline with no idle waiting between network I/O and data extraction. The piece also walks through the three Promise states, the all-or-nothing rejection rule, and five common mistakes including fake parallelism, forgotten catch handlers, and confusing result ordering.
The two-stage pattern is under-taught: most tutorials stop at parallel fetches and ignore that .json() is an async cost that can also be parallelized.
Promise.all's all-or-nothing semantics make it a poor default for user-facing UIs where one flaky endpoint shouldn't kill an entire page; allSettled is often the safer choice.
The 'fake parallelism' pitfall — writing two awaits back-to-back and assuming they run concurrently — is a common mental-model error that persists even among developers comfortable with async/await.