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 you can check the arithmetic.

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

What to check

Measure from the second request onward. The first request after pnpm start carries one-off startup cost, so its figures run high.

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 the same total work.

Memoization. Group 4 fetches one station from two sibling components. Identical fetch GETs are memoized for one render pass, so the upstream sees one request, not two. The timings do not show this, because the 2500ms delay is a local sleep that both components run in parallel.

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 for the slow one.

All three together

suspense fallback

one fallback for all three, so 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 after a 2500ms delay. Identical fetch GETs are memoized for one render pass, so this costs one request, not two.

First caller

suspense fallback

starts the request

Second caller

suspense fallback

joins the same request