๐Ÿณ IntuitiveFE
Login
โ† All concepts

SSE vs WebSocket vs Long Poll

โฑ๏ธ ~3-minute bite ยท solve the sandbox to master

0%lesson
๐Ÿง’

5-Year-Old Metaphor

โ€” The physical, real-world picture. No jargon.

Five ways to get news โ€” from calling every 5 minutes to a direct phone line.

๐Ÿ“ฌ

Polling

Calling a friend every 5 minutes to ask if they have news.

Simple. Works everywhere. Wastes calls when there's nothing new.

โณ

Long Polling

Calling and staying on hold until they have something to say.

Better. Responds the moment news arrives. But you're always on the line.

๐Ÿ“ป

SSE

Subscribing to a newsletter. They push to you when there's news.

Server pushes only. You can't talk back on the same channel. HTTP/1.1 compatible.

๐Ÿ“ก

WebSocket

A walkie-talkie. Both sides talk whenever they want.

Full duplex. Low overhead. No auto-reconnect โ€” you must implement it.

๐Ÿ”—

WebRTC

Two phones calling each other directly โ€” no exchange in between.

Peer-to-peer. Server steps out after handshake. Lowest latency. Complex setup.

๐ŸŽ›๏ธ

Interactive Sandbox

โ€” Move something, see it react instantly.

Protocol

Client makes a new HTTP request every N seconds. Server responds immediately, even if no new data (empty response). Simple but wasteful.

Message timeline

ClientChannelServer
GET /updatesโ†’
t=0ms โ€” HTTP request #1

Connection

New TCP connection per poll (unless HTTP/1

Latency

Up to N seconds (poll interval). Average N/2.

Scale

Poor at scale

Best for

Simple status checks

Gotcha: Polling creates a C10k problem โ€” each open connection holds server resources. Use long polling or SSE instead for anything realtime.
Insight: Polling is the only technique that works without any persistent connection. It's the right default when 'good enough' beats 'optimal', especially for internal dashboards with small user counts.
Explored:๐Ÿ“ฌโณ๐Ÿ“ป๐Ÿ“ก๐Ÿ”—
๐ŸŽฏ

Challenge

Step through all 5 protocols. Understand the connection model before comparing them.

Try it
๐ŸŽฏ

Why Should I Care?

โ€” The exact interview question + the bug it kills.

Interview questions

Q: Why does SSE have a 6-connection limit in HTTP/1.1?

HTTP/1.1 browsers limit concurrent connections to 6 per domain to prevent server overload. SSE connections are persistent, so 6 SSE connections consume all 6 slots. Opening a 7th tab blocks until one closes. HTTP/2 multiplexes all streams over one TCP connection โ€” the limit disappears. Fix: use HTTP/2, or use a shared SharedWorker with a single SSE connection across all tabs.

Q: What happens when a WebSocket connection drops?

js
1// WebSocket does NOT auto-reconnect โ€” you must implement it
2function createWebSocket(url) {
3 const ws = new WebSocket(url);
4 let reconnectDelay = 1000;
5ย 
6 ws.addEventListener('close', (e) => {
7 if (!e.wasClean) { // unexpected disconnect
8 console.log(`Reconnecting in ${reconnectDelay}ms...`);
9 setTimeout(() => createWebSocket(url), reconnectDelay);
10 reconnectDelay = Math.min(reconnectDelay * 2, 30000); // backoff
11 }
12 });
13ย 
14 ws.addEventListener('open', () => {
15 reconnectDelay = 1000; // reset on successful connect
16 });
17ย 
18 return ws;
19}
20ย 
21// EventSource (SSE) DOES auto-reconnect:
22const es = new EventSource('/events');
23// No reconnect logic needed โ€” browser handles it

Q: When is polling actually better than WebSocket?

Polling wins when: (1) update frequency is very low โ€” checking a job status every 30s doesn't justify a persistent connection; (2) infrastructure can't support persistent connections โ€” some serverless platforms (Vercel, Lambda) have hard timeouts of 30โ€“60s; (3) simplicity matters more than efficiency โ€” polling is debuggable in the Network tab without special tools; (4) CDN caching is valuable โ€” polling responses can be cached; WebSocket frames cannot.

Bug: no reconnection logic for SSE (but needed for WebSocket)

js
1// SSE: EventSource auto-reconnects. Last-Event-ID resumes stream.
2const es = new EventSource('/events');
3es.addEventListener('message', (e) => {
4 console.log('event:', e.data, 'id:', e.lastEventId);
5});
6// On reconnect, browser sends: Last-Event-ID: <last id>
7// Server resumes from that point
8ย 
9// WebSocket: no auto-reconnect. Missing this = silent data loss.
10const ws = new WebSocket(url);
11ws.onclose = () => {
12 // โœ— BAD: do nothing โ†’ connection lost forever
13 // โœ“ GOOD: setTimeout(() => reconnect(url), delay)
14};
๐Ÿ”ฌ

The Deep Dive

โ€” Spec refs, engine internals, the minutiae.

WebSocket handshake โ€” the 101 upgrade

js
1// Client sends:
2GET /chat HTTP/1.1
3Host: server.example.com
4Upgrade: websocket
5Connection: Upgrade
6Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
7Sec-WebSocket-Version: 13
8ย 
9// Server responds:
10HTTP/1.1 101 Switching Protocols
11Upgrade: websocket
12Connection: Upgrade
13Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
14// From this point: raw TCP frames, no HTTP overhead

SSE event format โ€” Last-Event-ID for resumption

js
1// SSE wire format (text/event-stream):
2id: 42
3ย 
4event: message
5ย 
6data: {"text":"hello","user":"alice"}
7ย 
8retry: 3000
9ย 
10ย 
11 โ† double newline ends the event
12ย 
13// Server-side (Node.js / Express):
14res.setHeader('Content-Type', 'text/event-stream');
15res.setHeader('Cache-Control', 'no-cache');
16res.setHeader('Connection', 'keep-alive');
17ย 
18function sendEvent(id, data) {
19 res.write(`id: ${id}
20data: ${JSON.stringify(data)}
21ย 
22`);
23}
24ย 
25// Client auto-resumes on reconnect:
26req.headers['last-event-id'] // pick up from here

WebRTC signaling + ICE candidate exchange

js
1// Step 1: Client A creates offer (via signaling server)
2const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
3const offer = await pc.createOffer();
4await pc.setLocalDescription(offer);
5signalingServer.send({ type: 'offer', sdp: offer });
6ย 
7// Step 2: Client B receives offer, creates answer
8const pc = new RTCPeerConnection({ iceServers: [...] });
9await pc.setRemoteDescription(offer);
10const answer = await pc.createAnswer();
11await pc.setLocalDescription(answer);
12signalingServer.send({ type: 'answer', sdp: answer });
13ย 
14// Step 3: ICE candidates exchanged until P2P established
15pc.onicecandidate = (e) => {
16 if (e.candidate) signalingServer.send({ type: 'ice', candidate: e.candidate });
17};

Comparison table

ProtocolDirectionLatencyHTTP/1.1Auto-reconnectServer state
Pollingโ†’โ†N/2 avgโœ“N/AStateless
Long Pollingโ†’โ†Near 0โœ“N/AHolds connection
SSEโ†Near 0โœ“AutoHolds stream
WebSocketโ†”<10msโœ“NoStateful session
WebRTCโŸท<50msโœ“ICEP2P after signal

Socket.io โ€” abstraction layer

Socket.io wraps WebSocket with automatic fallback (long polling), auto-reconnect, room-based broadcasting, and acknowledgements. It adds ~30KB to bundle but eliminates the reconnection boilerplate. Use raw WebSocket for control; Socket.io for rapid development.

๐ŸŽค

Interview Questions

โ€” Real questions from real interviews โ€” with answers.

WebSocket with a pub/sub broker (Redis); fan out per-document, not per-user.

SSE for server-push only (feeds, notifications); WebSocket when the client also sends data.

Buffer with a bounded queue; drop or throttle when the queue is full; use binary framing to reduce size.

WebSocket connection goes stale on sleep; no auto-reconnect means silent data loss.

Authoritative server holds game state; clients send intents; server broadcasts deltas.

๐ŸŽฎ

Memory Game

โ€” Quick quiz โ€” lock the concept in long-term memory.
1/4

What is exponential backoff with jitter used for in WebSocket clients?