惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cisco Talos Blog
Cisco Talos Blog
V
V2EX
C
Check Point Blog
GbyAI
GbyAI
D
Docker
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog RSS Feed
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
Netflix TechBlog - Medium
T
Troy Hunt's Blog
博客园 - Franky
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Microsoft Security Blog
Microsoft Security Blog
P
Privacy & Cybersecurity Law Blog
WordPress大学
WordPress大学
The Cloudflare Blog
S
SegmentFault 最新的问题
Latest news
Latest news
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
I
InfoQ
博客园 - 【当耐特】
NISL@THU
NISL@THU
A
About on SuperTechFans
T
Tailwind CSS Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Scott Helme
Scott Helme
雷峰网
雷峰网
C
CXSECURITY Database RSS Feed - CXSecurity.com
Security Latest
Security Latest
V
Vulnerabilities – Threatpost
Security Archives - TechRepublic
Security Archives - TechRepublic
A
Arctic Wolf
Hacker News: Ask HN
Hacker News: Ask HN
N
News and Events Feed by Topic
IT之家
IT之家
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
aimingoo的专栏
aimingoo的专栏
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
阮一峰的网络日志
阮一峰的网络日志
SecWiki News
SecWiki News
大猫的无限游戏
大猫的无限游戏
S
Security Affairs
The Register - Security
The Register - Security
www.infosecurity-magazine.com
www.infosecurity-magazine.com
L
LINUX DO - 热门话题
T
Tor Project blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Real-Time on the Frontend - SSE, WebSockets & Polling
Yogesh Yadav · 2026-05-03 · via DEV Community

Real-time doesn't always mean WebSockets. Picking the right tool changes everything.

In this article we'll cover when polling is actually the right answer, why SSE is the most underrated real-time technology on the web, when WebSockets are genuinely worth the complexity, how to handle reconnections properly, lessons from building real-time features at scale and a clear decision framework so you stop defaulting to WebSockets out of habit.


Every time a developer hears "real-time", the next word out of their mouth is usually "WebSockets."

I get it. WebSockets feel like the serious, production-grade choice. Polling feels naive. SSE feels like something you read about once and forgot. So the default becomes WebSockets and teams end up maintaining infrastructure complexity they didn't need for a problem that SSE would have solved in 30 lines.

I've built real-time features on platforms handling more than 10 million active users. Subscription verification flows, live event score updates, notification systems. Different problems, different tools. And the most important lesson I've taken from all of it is this: pick the simplest technology that actually solves the problem.

This article will help you do that.


Polling - Not Always the Wrong Answer

Polling gets dismissed quickly. "Just use WebSockets" is the usual response. But polling has a place and understanding when it's appropriate saves you from over-engineering.

Short Polling

The simplest form. Make a request every N seconds, get a response, repeat.

function startPolling(interval = 5000) {
  return setInterval(async () => {
    const data = await fetch('/api/status').then(res => res.json())
    updateUI(data)
  }, interval)
}

Enter fullscreen mode Exit fullscreen mode

This works fine when:

  • The data changes infrequently and slight delays are acceptable.
  • You have a small number of concurrent users.
  • The implementation complexity budget is low and you need something working fast.

Where it falls apart is scale. If 100,000 users are polling every 5 seconds, that's 20,000 requests per second hitting your server for data that probably hasn't changed. At that point polling isn't simple anymore - it's expensive.

Smarter Polling

If you're going to poll, at least do it intelligently. Back off when the tab is hidden, increase the interval when responses show no change, stop entirely when the user is inactive.

function smartPoll(fetchFn, options = {}) {
  const { baseInterval = 5000, maxInterval = 60000 } = options
  let currentInterval = baseInterval
  let timeoutId = null

  async function poll() {
    if (document.hidden) {
      timeoutId = setTimeout(poll, maxInterval)
      return
    }

    const data = await fetchFn()
    currentInterval = data.changed ? baseInterval : Math.min(currentInterval * 1.5, maxInterval)
    timeoutId = setTimeout(poll, currentInterval)
  }

  poll()

  return () => clearTimeout(timeoutId)
}

Enter fullscreen mode Exit fullscreen mode

This alone can cut unnecessary requests significantly without changing the fundamental approach.

When to use polling: low-frequency updates, small user base, dashboard data that refreshes every minute or so, situations where you need something working today and can revisit later.


SSE - The Underrated Middle Ground

Server-Sent Events is the technology most developers skip over on their way to WebSockets. That's a mistake.

SSE is a one-directional, HTTP-native protocol. The server pushes data to the client over a persistent connection. The client listens. That's it.

What makes SSE genuinely compelling:

  • It runs over regular HTTP. No protocol upgrade, no special infrastructure, no load balancer configuration changes.
  • The browser handles reconnection automatically. If the connection drops, the browser retries with the last event ID so you don't miss events.
  • It works through HTTP/2 multiplexing, meaning multiple SSE connections share a single TCP connection.
  • It's dramatically simpler to implement than WebSockets on both the client and server side.
function connectToEventStream(url, handlers) {
  const eventSource = new EventSource(url, { withCredentials: true })

  eventSource.onopen = () => {
    console.log('SSE connection established')
  }

  eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data)
    handlers.onMessage(data)
  }

  eventSource.onerror = (err) => {
    if (eventSource.readyState === EventSource.CLOSED) {
      handlers.onClose()
    }
  }

  // Listen to named events
  eventSource.addEventListener('subscription-update', (event) => {
    handlers.onSubscriptionUpdate(JSON.parse(event.data))
  })

  return () => eventSource.close()
}

Enter fullscreen mode Exit fullscreen mode

The server side is equally straightforward. Any server that can keep an HTTP connection alive and stream data to it can serve SSE. No WebSocket library needed.

// Express example
app.get('/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream')
  res.setHeader('Cache-Control', 'no-cache')
  res.setHeader('Connection', 'keep-alive')

  const sendEvent = (eventName, data) => {
    res.write(`event: ${eventName}\n`)
    res.write(`data: ${JSON.stringify(data)}\n\n`)
  }

  // Send initial state
  sendEvent('connected', { timestamp: Date.now() })

  // Clean up on disconnect
  req.on('close', () => {
    // Remove this client from your subscriber list
  })
})

Enter fullscreen mode Exit fullscreen mode

Where SSE Shines at Scale

I've used SSE for subscription verification flows where the client needs to know the moment a payment has been confirmed on the backend. The user completes a payment, the frontend opens an SSE connection and the moment the backend confirms the transaction, it pushes an event. The client reacts immediately.

No polling loop hammering the endpoint. No WebSocket infrastructure. Just an HTTP connection that the server writes to when something happens.

SSE is the right tool for: notifications, live feed updates, subscription and payment status, progress tracking for long-running operations, any scenario where data flows server-to-client and you don't need the client to send messages back in real time.

The one real limitation: SSE is one-directional. The client cannot send data back over the same connection. If you need bidirectional communication, you need WebSockets.


WebSockets - When You Actually Need Them

WebSockets give you a full-duplex connection. Both the client and server can send messages at any time over a single persistent connection. That's genuinely powerful and genuinely more complex to operate.

class WebSocketClient {
  constructor(url) {
    this.url = url
    this.socket = null
    this.reconnectAttempts = 0
    this.maxReconnectAttempts = 5
    this.listeners = new Map()
  }

  connect() {
    this.socket = new WebSocket(this.url)

    this.socket.onopen = () => {
      console.log('WebSocket connected')
      this.reconnectAttempts = 0
    }

    this.socket.onmessage = (event) => {
      const message = JSON.parse(event.data)
      const handler = this.listeners.get(message.type)
      if (handler) handler(message.payload)
    }

    this.socket.onclose = () => {
      this.reconnect()
    }

    this.socket.onerror = (err) => {
      console.error('WebSocket error', err)
    }
  }

  send(type, payload) {
    if (this.socket?.readyState === WebSocket.OPEN) {
      this.socket.send(JSON.stringify({ type, payload }))
    }
  }

  on(type, handler) {
    this.listeners.set(type, handler)
  }

  reconnect() {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      console.error('Max reconnect attempts reached')
      return
    }

    const delay = Math.min(1000 * 2 ** this.reconnectAttempts, 30000)
    this.reconnectAttempts++

    setTimeout(() => this.connect(), delay)
  }

  disconnect() {
    this.socket?.close()
  }
}

Enter fullscreen mode Exit fullscreen mode

WebSockets are the right tool for:

  • Chat and messaging - messages flow in both directions in real time.
  • Collaborative editing - multiple users editing the same document simultaneously.
  • Live gaming - state needs to sync between players with minimal latency.
  • Live auctions or trading - both the server and client are pushing updates constantly.

What most tutorials don't tell you about WebSockets:

Load balancers need sticky sessions or a pub/sub layer. A WebSocket connection is stateful - it lives on one server instance. If you're running multiple server instances behind a load balancer, a message published on instance A won't reach clients connected to instance B unless you have a message broker like Redis pub/sub in between.

At scale, this infrastructure cost is real. It's manageable, but it's not free, and it's worth knowing about before you commit to WebSockets for a use case that SSE would have handled.


Reconnection - The Part Everyone Gets Wrong

Every real-time implementation needs to handle disconnections. Networks drop. Servers restart. Mobile users switch between WiFi and cellular. If your reconnection logic is wrong, users silently stop receiving updates with no indication that anything is broken.

A few things that matter:

Exponential backoff with jitter. Don't retry on a fixed interval. If your server goes down and 100,000 clients all retry every 5 seconds in perfect synchrony, they'll create a thundering herd the moment the server comes back up.

function getReconnectDelay(attempt) {
  const base = Math.min(1000 * 2 ** attempt, 30000)
  const jitter = Math.random() * 1000
  return base + jitter
}

Enter fullscreen mode Exit fullscreen mode

The jitter spreads reconnection attempts across time so your server isn't hit by a simultaneous spike.

Track the last received event. For SSE, the browser does this for you with the Last-Event-ID header. For WebSockets, you need to implement this yourself. When you reconnect, tell the server where you left off so it can replay missed events.

Distinguish between clean closes and unexpected drops. A user navigating away is a clean close - don't reconnect. A network error is unexpected - do reconnect. Handle them differently.

Show the user when the connection is lost. A silent failure is worse than a visible one. If the real-time connection drops and doesn't recover quickly, tell the user. A small "reconnecting…" indicator is far better than them wondering why the live scores stopped updating.


Lessons From Building Real-Time at Scale

A few things you learn when real-time features are running for millions of users that don't show up in tutorials:

Connection count is a resource. Every open SSE or WebSocket connection holds state on the server. At scale, you need to think about connection limits, memory per connection and what happens when a deployment causes all connections to drop and reconnect simultaneously.

Health checks matter. Long-lived connections can appear alive to both sides while being silently broken in the middle - a dropped connection that neither end detected. Implement a heartbeat: the server sends a ping event every 30 seconds, the client expects it. If it doesn't arrive, reconnect.

// Server heartbeat
setInterval(() => {
  clients.forEach(client => {
    client.write('event: ping\ndata: {}\n\n')
  })
}, 30000)

// Client - restart connection if no ping received
let lastPing = Date.now()

eventSource.addEventListener('ping', () => {
  lastPing = Date.now()
})

setInterval(() => {
  if (Date.now() - lastPing > 45000) {
    eventSource.close()
    reconnect()
  }
}, 10000)

Enter fullscreen mode Exit fullscreen mode

Authentication on long-lived connections needs thought. Tokens expire. If a user's access token expires while they have an open WebSocket connection, how do you handle that? You need a mechanism to refresh the token and either send it over the existing connection or re-establish with a new one.

Test with realistic concurrency. A real-time feature that works perfectly with 10 concurrent connections can behave completely differently with 10,000. Load test before you ship.


What Else Is Out There

WebRTC

WebRTC is a different category entirely. Where SSE and WebSockets are server-client protocols, WebRTC enables peer-to-peer communication directly between browsers. Audio, video and data can flow between clients without going through your server.

If you're building video calling, live audio or peer-to-peer file sharing, WebRTC is the right tool. It's not a replacement for SSE or WebSockets for server-push use cases - it solves a fundamentally different problem.

WebTransport

WebTransport is a newer protocol built on HTTP/3 and QUIC. It offers lower latency than WebSockets, supports both reliable and unreliable message delivery and handles connection migration better on mobile networks. It's promising, but browser support is still maturing. Worth keeping an eye on for the next few years.

GraphQL Subscriptions

If your stack uses GraphQL, subscriptions give you real-time updates through a familiar query interface. Under the hood they typically run over WebSockets. Useful if you're already invested in GraphQL and want real-time without adding a separate protocol layer.


How to Pick the Right Approach

Here's the decision framework I use:

Do you need the client to send messages to the server in real time?

  • No - SSE is probably enough. Start there.
  • Yes - WebSockets.

How frequently does data change?

  • Every few minutes or less - polling with a sensible interval.
  • Continuously or on server events - SSE or WebSockets.

What's your infrastructure complexity budget?

  • Low - polling or SSE. Both work over standard HTTP.
  • Higher - WebSockets, with the understanding that you'll need to handle load balancing and state carefully.

Do you need peer-to-peer communication?

  • Yes - WebRTC.
  • No - SSE or WebSockets.

Start simple. Polling is not embarrassing. SSE handles more use cases than most developers realize. WebSockets are powerful but come with operational overhead that you should only take on when you genuinely need what they offer.

The real-time feature that works reliably in production is always better than the one built with the most impressive technology.


Final Thoughts

Real-time on the frontend is not a single technology decision. It's a series of decisions, made per feature, based on what that feature actually needs.

SSE is HTTP-native, automatically reconnecting and dramatically simpler than WebSockets for one-directional data flows. Most real-time use cases are one-directional. Start there.

Use WebSockets when you genuinely need bidirectional communication. Understand the infrastructure implications before you commit.
And never underestimate polling for the right use case. At low frequency and low scale, it's often the most maintainable solution you can ship.

Pick the simplest tool that solves the problem. Your future self - and the engineer who maintains this after you - will thank you.


Have thoughts or questions on real-time frontend? Drop them in the comments, always happy to discuss.

This article is part of the Frontend at Scale series.