Streaming and waterfalls

Three upstream calls with injected delays of 1200ms, 1800ms, 2400ms, arranged four different ways. The upstream figure on each panel is measured wall-clock time, so the arithmetic is checkable rather than asserted.

Sequential should total about 5400ms. Parallel should be about 2400ms — the slowest leg, not the sum.

What to check

Measure from the second request onward. The first request after pnpm start can serve build-warm data with timings that make no sense. This bit during development: one reading showed legs of 985ms, 27ms and 27ms before settling into the real numbers.

The waterfall. Compare the two panels in the first group. Same three delays; one totals about 5400ms and the other about 2400ms, purely because of Promise.all.

Boundary granularity. Watch groups 2 and 3 on a hard reload. One boundary means all three appear together at 2400ms. Three boundaries mean they appear at 1200ms, 1800ms and 2400ms — the first content lands twice as early for identical total work.

Memoization. Group 4 fetches one station from two sibling components. Both report roughly the same duration and the page does not take 5000ms, because only one HTTP request was made.

See it in the raw stream. curl -N --raw -s localhost:3000/streaming, or time the whole page with curl -s -o /dev/null -w '%{time_total}' localhost:3000/streaming.

1 · Sequential against parallel

Both panels fetch three stations with the same three delays. The left awaits each in turn; the right uses Promise.all. They use different stations so neither can reuse the other’s in-flight requests.

Sequential

suspense fallback

expect about 5400ms

Parallel

suspense fallback

expect about 2400ms

2 · One boundary for three slow things

Fetched in parallel, but rendered behind a single boundary — so the two fast results wait on the slow one for no reason.

All three together

suspense fallback

one fallback for all three — nothing appears until about 2400ms

3 · One boundary each

The same three calls and the same total time, but each result paints as soon as it is ready.

Item 1

suspense fallback

resolves at about 1200ms

Item 2

suspense fallback

resolves at about 1800ms

Item 3

suspense fallback

resolves at about 2400ms

4 · Memoized duplicates

Both panels fetch the same station with a 2500ms delay. Identical fetch GETs are memoized for one render pass, so this costs one request, not two — and the page does not take 5000ms.

First caller

suspense fallback

starts the request

Second caller

suspense fallback

joins the same request