Part I β The Pipes Β· Lesson 2.2
Short Polling
In one lineAsk the server every N seconds. Simple, stateless, and mostly wasted requests.
Where you've seen itThe unread-count badge in a webmail tab, quietly hitting /unread every 60 seconds whether or not anything changed.
Diagram
SHORT POLLING β the client drives the clock
0s 5s 10s 15s 20s 25s
βββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
REQ REQ REQ REQ REQ REQ
β β β β β β
304 304 304 304 200β
304
empty empty empty empty DATA empty
6 round trips Β· 1 useful Β· worst-case staleness = the interval (5s)THE COST AT SCALE
100,000 clients Γ 1 request / 5s = 20,000 req/s
...to deliver maybe 50 real updates per second.
ββββββββββββββββββββββββββββββββββββββββββββββ
β βββββββββββββββββββββββββββββββββββββββββ β
β 99.75% empty responses 0.25% payload β
ββββββββββββββββββββββββββββββββββββββββββββββHow it works
setInterval(or better, asetTimeoutchain) fires a request.- The server answers immediately β with data or with nothing.
- Send
ETag/If-None-Matchso empty answers cost a304and no body. - Back off when the tab is hidden (
document.visibilityState) and on repeated empty responses. - Chain timeouts rather than using
setInterval, so a slow response can't stack requests on top of each other.
async function poll(url, ms = 5000) {
while (document.visibilityState === 'visible') {
const res = await fetch(url, { headers: { 'If-None-Match': etag } });
if (res.status === 200) render(await res.json());
await new Promise((r) => setTimeout(r, ms)); // wait AFTER the response
}
}Trade-offs
| Use when | Avoid when |
|---|---|
| Updates are rare and staleness is fine | You need sub-second freshness |
| You want zero server state | Client count is large (request storm) |
| Infrastructure is plain HTTP + CDN | Battery and mobile data matter |
| The feature must ship today | Many clients poll on the same boundary |
The thundering-herd trap: every client polling on a round interval synchronises after a deploy and hits the server in one spike. Add jitter β ms + Math.random() * 1000.
Interview angle
"What's wrong with
setInterval(fetchData, 1000)?"
- Responses slower than the interval stack up; use a
setTimeoutchain that waits for the response. - It keeps polling in a hidden tab β gate on
visibilityStateand back off. - No jitter means synchronised clients hammer the server in waves.
Recap
- The client owns the clock; freshness is capped by the interval.
- Cheap to build, expensive to run β cost scales with clients, not with events.
- Fix the three defaults: chain timeouts, honour visibility, add jitter.