Networking
Long Polling
Real-time updates via HTTP request holding
Intermediate
http real-time polling fallback
Definition
Long polling is a technique where the client makes an HTTP request and the server holds the connection open until new data is available. Once data is sent (or a timeout occurs), the client immediately makes another request, creating a near real-time communication channel over standard HTTP.
How It Works
Traditional Polling: Long Polling:
Request 1 ────> Request 1 ────>
<─── Response <─────────────── Response
(wait 5s) (held 30s until data)
Request 2 ────> Request 2 ────>
<─── Response <─────────────── Response
(wait 5s) (held until data)
High latency, Low latency,
many empty responses efficient
Implementation
Client-Side
async function longPoll() {
try {
const response = await fetch('/api/poll', {
// Long timeout
signal: AbortSignal.timeout(60000)
});
const data = await response.json();
// Handle received data
handleUpdate(data);
} catch (error) {
if (error.name === 'TimeoutError') {
console.log('Long poll timeout, reconnecting...');
} else {
console.error('Poll error:', error);
// Wait before retry
await sleep(5000);
}
} finally {
// Immediately poll again
longPoll();
}
}
// Start polling
longPoll();
Server-Side
// Node.js with Express
app.get('/api/poll', async (req, res) => {
const clientId = req.query.clientId;
// Set headers to prevent buffering
res.setHeader('Content-Type', 'application/json');
res.setHeader('Cache-Control', 'no-cache');
try {
// Wait for data (with timeout)
const data = await Promise.race([
waitForData(clientId),
sleep(30000) // 30 second timeout
]);
if (data) {
res.json({
type: 'update',
data
});
} else {
// Timeout - send empty to trigger reconnect
res.json({ type: 'timeout' });
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Data waiting mechanism
const pendingRequests = new Map();
function waitForData(clientId) {
return new Promise((resolve) => {
pendingRequests.set(clientId, resolve);
});
}
// When new data arrives
function notifyClients(data) {
for (const [clientId, resolve] of pendingRequests) {
resolve(data);
}
pendingRequests.clear();
}
Comparison
vs Regular Polling
// Regular polling - wasteful
setInterval(async () => {
const data = await fetch('/api/data');
// Most responses: "no new data"
}, 5000); // Check every 5 seconds
// Long polling - efficient
async function longPoll() {
const data = await fetch('/api/poll'); // Waits for data
handleData(data);
longPoll(); // Reconnect immediately
}
// Only makes requests when needed
vs WebSockets
// Long polling
// ✅ Works with HTTP proxies/firewalls
// ✅ Simple to implement
// ✅ Auto-reconnect built-in
// ❌ Higher latency than WebSockets
// ❌ More overhead per message
// WebSockets
// ✅ Lower latency
// ✅ Bidirectional
// ✅ Lower overhead
// ❌ Proxy/firewall issues
// ❌ More complex
Use Cases
When to Use Long Polling
✅ Need real-time but WebSockets not available
✅ Strict HTTP-only environment
✅ Simple implementation needed
✅ Fallback for WebSocket failures
✅ Behind restrictive proxies
❌ High-frequency updates (use WebSockets)
❌ Bidirectional communication (use WebSockets)
❌ Binary data (use WebSockets)
Best Practices
1. Timeout Management
// Client timeout should be longer than server
const CLIENT_TIMEOUT = 35000; // 35 seconds
const SERVER_TIMEOUT = 30000; // 30 seconds
// This prevents both sides timing out simultaneously
2. Reconnection Backoff
let retryDelay = 1000;
async function poll() {
try {
const data = await fetchWithTimeout('/api/poll', 35000);
handleData(data);
retryDelay = 1000; // Reset on success
} catch (error) {
console.error('Poll failed, retrying in', retryDelay);
await sleep(retryDelay);
retryDelay = Math.min(retryDelay * 2, 30000); // Max 30s
}
poll();
}
3. Connection Cleanup
// Server cleanup on disconnect
req.on('close', () => {
clearTimeout(timeoutId);
pendingRequests.delete(clientId);
console.log(`Client ${clientId} disconnected`);
});
Modern Alternatives
Use SSE Instead (If Possible)
// SSE is simpler for server->client
const source = new EventSource('/api/events');
source.onmessage = (e) => handleData(JSON.parse(e.data));
// Auto-reconnects, simpler protocol
Use WebSockets for Bidirectional
// For chat, gaming, collaborative editing
const ws = new WebSocket('wss://api.example.com');
ws.onmessage = (e) => handleData(e.data);
ws.send(JSON.stringify({ type: 'message', text: 'Hello' }));
Key Takeaway
Long polling provides real-time updates over standard HTTP by holding requests open until data is available. It’s a good fallback when WebSockets aren’t supported, but modern alternatives like SSE offer simpler implementations for server-to-client scenarios. Use long polling when you need HTTP compatibility and can tolerate slightly higher latency.