El polling es un modelo de pull donde el cliente pregunta repetidamente; SSE y WebSockets mantienen una conexión abierta y el servidor hace push en tiempo real. Elegir entre ellos son en realidad tres preguntas — en qué dirección fluyen los datos, con qué frecuencia cambian y qué infraestructura hay en medio — y la respuesta honesta difiere por funcionalidad, no por app.
Puntos clave
| Pregunta | Respuesta |
|---|---|
| Short polling | Preguntar con un timer — simple, amigable con el caché, solicitudes en su mayoría desperdiciadas |
| Long polling | El servidor retiene la solicitud hasta que llegan datos — push simulado sobre HTTP plano |
| SSE | Un stream HTTP, eventos de texto servidor → cliente, reconexión automática incluida |
| WebSockets | Un socket, full-duplex, capaz de binario — el protocolo es tuyo |
| La regla práctica | Un sentido → SSE · dos sentidos → WebSockets · cambios raros → el polling está bien |
El código, lado a lado
// 1 · Short polling — preguntar con un timer
setInterval(async () => {
const res = await fetch('/api/messages?since=' + lastId);
render(await res.json()); // casi siempre vacío — los headers se pagan igual
}, 2000);
// 2 · Server-Sent Events — un stream HTTP, el servidor hace push de eventos de texto
const events = new EventSource('/api/stream');
events.onmessage = (e) => render(JSON.parse(e.data)); // se reconecta solo
// 3 · WebSocket — un socket, ambas direcciones
const ws = new WebSocket('wss://api.example.com/live');
ws.onmessage = (e) => render(JSON.parse(e.data));
ws.send(JSON.stringify({ type: 'typing' })); // el cliente también hace push
Lo que la mayoría de las apps realmente publica — una capa de suscripción que es dueña del transporte por ti:
// JavaScript / Node.js — Back4app JS SDK
// The polling replacement: subscribe once, receive pushes
const query = new Parse.Query('Message');
query.equalTo('room', 'general');
const subscription = await query.subscribe(); // WebSocket under the hood
subscription.on('create', (msg) => render(msg)); // pushed, not polled
// subscription.unsubscribe() when the screen closes // Flutter / Dart — Back4app Flutter SDK
// The polling replacement: subscribe once, receive pushes
final liveQuery = LiveQuery();
final query = QueryBuilder<ParseObject>(ParseObject('Message'))
..whereEqualTo('room', 'general');
final subscription = await liveQuery.client.subscribe(query); // WebSocket under the hood
subscription.on(LiveQueryEvent.create, (msg) => render(msg)); // pushed, not polled // iOS / Swift — Back4app Swift SDK
// The polling replacement: subscribe once, receive pushes
let query = Message.query("room" == "general")
let subscription = try await query.subscribe() // WebSocket under the hood
subscription.handleEvent { _, event in
if case .created(let msg) = event { render(msg) } // pushed, not polled
} // Android / Kotlin — Back4app Android SDK
// The polling replacement: subscribe once, receive pushes
val client = ParseLiveQueryClient.Factory.getClient()
val query = ParseQuery.getQuery<ParseObject>("Message")
query.whereEqualTo("room", "general")
val subscription = client.subscribe(query) // WebSocket under the hood
subscription.handleEvent(SubscriptionHandling.Event.CREATE) { _, msg ->
render(msg) // pushed, not polled
} Cómo funciona cada técnica
El short polling es un setInterval alrededor de un fetch: preguntar cada n segundos si algo cambió. Cada ciclo paga un request/response HTTP completo — headers, auth, enrutamiento — casi siempre para escuchar “nada todavía”, y el retraso promedio de entrega es la mitad del intervalo más un viaje de ida y vuelta.
El long polling muda la espera al servidor: el cliente pregunta, el servidor retiene la solicitud abierta hasta que llegan datos o vence un timeout, y el cliente vuelve a preguntar de inmediato. Aproxima el push sobre HTTP puro — el rodeo de la era Comet, con sus costos conocidos (churn de reconexiones, headers por solicitud, cuidado con el orden) catalogados en el RFC 6202.
Los Server-Sent Events hacen permanente la respuesta retenida: una respuesta HTTP con Content-Type: text/event-stream que nunca termina, por la que el servidor escribe eventos UTF-8 (campos data:, event:, id:, retry:, según el estándar WHATWG). El EventSource del navegador la consume — y se reconecta automáticamente, reanudando vía Last-Event-ID.
Los WebSockets abandonan HTTP por completo: un handshake hace upgrade de la conexión (101 Switching Protocols, RFC 6455), tras lo cual ambos lados intercambian frames de texto o binarios con ~2–14 bytes de overhead, full-duplex, hasta que alguien cierra el socket. Capacidad máxima — y todo lo que está por encima del frame (formato de mensajes, acks, reconexión, reanudación) queda para que tú lo diseñes.
SSE vs. WebSockets vs. long polling vs. short polling
| Short polling | Long polling | SSE | WebSockets | |
|---|---|---|---|---|
| Dirección | Pull | Push simulado | Servidor → cliente | Full-duplex |
| Protocolo | HTTP plano | HTTP plano | Stream HTTP plano | Protocolo propio tras el upgrade |
| Latencia de entrega | intervalo/2 + RTT | ~RTT | ~RTT | ~RTT |
| Payloads | Cualquiera | Cualquiera | Solo texto UTF-8 | Texto + binario |
| Reconexión automática | Trivial (el siguiente poll) | Ciclo de re-solicitud | Incluida + Last-Event-ID | La escribes tú |
| Fricción con proxies/firewalls | Ninguna | Baja | Baja (salvedades de buffering) | El upgrade puede ser eliminado |
| Estado en el servidor | Ninguno | Solicitudes retenidas | Streams abiertos | Sockets anclados |
| Complejidad | Trivial | Moderada | Baja | La más alta |
| Punto dulce | Cambios raros | Fallback legacy | Feeds, notificaciones, streams de AI | Chat, juegos, colaboración |
¿Cuánto cuesta el polling? La matemática del overhead
La comparación que ninguna página de ranking corre — 1.000 clientes con un poll de 2 segundos versus los mismos 1.000 con push:
1.000 clientes, polling de 2 s 1.000 clientes, push
→ 500 solicitudes/segundo sostenidas → 0 solicitudes en reposo
→ ~800 B de headers por ciclo → framing de evento SSE ~5 B
→ ~0,4 MB/s de puro impuesto de headers → overhead de frame WebSocket 2–14 B
→ casi todas las respuestas vacías → los bytes fluyen solo cuando hay datos
→ retraso promedio de entrega: 1 s + RTT → retraso de entrega: ~RTT
La lección corta en ambos sentidos. El push gana de forma aplastante cuando las actualizaciones son frecuentes y la latencia importa. Pero si los datos cambian cada hora, esas 500 solicitudes/s nunca se materializan — un poll suave (o refetch al enfocar) es amigable con el caché, depurable con curl, compatible con serverless y libre de estado de conexión. Descartar el polling por completo es moda, no ingeniería.
Reconexión: el diferenciador silencioso
Las conexiones se caen — handoffs de celular, tapas de laptop, timeouts de inactividad en proxies (a menudo 30–120 s, y por eso los streams SSE envían comentarios de keep-alive y los WebSockets intercambian frames ping/pong). Lo que pasa después separa a los transportes. El contrato de SSE lo maneja: el navegador reintenta con el retraso retry fijado por el servidor y presenta Last-Event-ID, así que un servidor que guarda un buffer corto de eventos reanuda el stream sin huecos. Un WebSocket crudo simplemente se cierra: la reconexión con backoff exponencial, la recuperación de mensajes perdidos y el protocolo de reanudación son todos código de aplicación — el renglón más subestimado de “solo usaremos WebSockets”. En cualquier caso, un cliente que estuvo offline por minutos necesita ponerse al día (volver a correr la consulta base), no solo reconectarse — los transportes de push entregan deltas, y los deltas suponen una línea base.
Por qué el chat con AI transmite sobre SSE
La salida de un modelo token a token es la carga perfecta para SSE: estrictamente unidireccional, texto, en ráfagas, sobre HTTP plano que todo proxy y CDN entiende, con semántica de reanudación para generaciones caídas. Por eso las APIs de LLM transmiten sus respuestas de forma abrumadora como text/event-stream — y por eso el patrón vale la pena conocerlo más allá de los chatbots: feeds de progreso, logs de builds y dashboards comparten la misma forma. Los WebSockets entran en los productos de AI en la capa de voz, donde el usuario interrumpe a mitad del stream — en el momento en que el cliente necesita responder, el impuesto del duplex compra algo.
Escalar el push: por qué importa el estado
El polling y SSE son HTTP común para un balanceador de carga; cualquier servidor puede responder cualquier solicitud (SSE sostiene un stream pero conserva la semántica HTTP, y solo necesita proxies sin buffering). Los WebSockets son estado: cada socket ancla un cliente a un proceso, así que escalar horizontalmente implica balanceo consciente de conexiones, drenado en los deploys y un backplane de pub/sub para que un mensaje publicado en el servidor A alcance los sockets que sostiene el servidor B. Sobre HTTP/1.1, SSE tiene su propio tropiezo famoso — seis conexiones por origen compartidas entre pestañas (la advertencia de MDN) — retirado por HTTP/2, donde los streams se multiplexan sobre una conexión. Nada de esto es exótico; todo esto es la razón por la que existen las capas de tiempo real gestionadas.
Casos de uso comunes
- Feeds de notificaciones y tickers — un sentido, texto, frecuente: el terreno propio de SSE.
- Streaming de respuestas de AI — el caso estrella actual de SSE; una dirección, texto de tokens, reanudación al caerse.
- Chat y colaboración — los mensajes fluyen en ambos sentidos con baja latencia: WebSockets, normalmente vía una capa gestionada.
- Dashboards en vivo — push del servidor con resultados de consultas; en la práctica una suscripción de live query en lugar de un transporte hecho a mano.
- Datos que cambian lento — inventario que se actualiza cada hora, pantallas de configuración: polling o refetch al enfocar, honestamente.
¿Qué transporte deberías usar? Matriz de decisión
| Tu situación | Elige |
|---|---|
| Solo servidor → cliente (feeds, streams, progreso) | SSE |
| Cliente y servidor envían ambos en tiempo real | WebSockets |
| Actualizaciones más raras que cada pocos minutos | Short polling / refetch al enfocar |
| Proxies hostiles, infraestructura legacy | Long polling como fallback |
| Payloads binarios o de alta frecuencia | WebSockets |
| Plataforma serverless, timeouts de funciones | Polling o SSE vía hosts capaces de streaming |
| ”Solo quiero datos en vivo en pantalla” | Una capa de live queries que sea dueña del transporte |
Limitaciones y trade-offs
- SSE es solo texto y un solo sentido. El binario necesita codificación; cualquier charla del cliente al servidor viaja en solicitudes HTTP aparte — bien para acks, mal para chat.
- Los WebSockets te meten en el negocio de los protocolos. Framing, acks, reconexión, reanudación, backpressure: el transporte es fácil, el contrato encima es el trabajo.
- La simplicidad del polling esconde un piso de latencia. Ningún ajuste escapa al retraso promedio de intervalo/2; achicar el intervalo solo recompra el impuesto de headers.
- El long polling es lo peor de ambos a escala. Las solicitudes retenidas consumen capacidad del servidor como el push, mientras pagan overhead de reconexión por mensaje como el pull — por eso sobrevive solo como peldaño de fallback.
- Todos los transportes de push necesitan la cooperación de los proxies. El buffering de streams rompe SSE en silencio; los headers de upgrade eliminados rompen WebSockets — prueba a través de la infraestructura real, no de localhost.
Transportes en tiempo real en Back4app
Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. La decisión de transporte queda en gran parte absorbida por las Live Queries: las pestañas de código de arriba se suscriben a una consulta y reciben eventos por push sobre una flota de WebSockets que la plataforma opera — conexiones, reconexiones, chequeos de permisos y fan-out entre servidores incluidos — de modo que “¿SSE o WebSockets?” se convierte en un detalle de implementación que heredas y no en infraestructura que construyes. Donde una cadencia más suave realmente encaja, la misma consulta corre como un fetch plano en tu propio calendario; hacer polling de una consulta de Parse y suscribirse a ella están a una línea de distancia, lo que convierte el transporte correcto por funcionalidad en un refactor, no en una reescritura.
Preguntas frecuentes
¿Qué es mejor, SSE o WebSockets?
Ninguno universalmente — la pregunta es la dirección. SSE es la herramienta más simple cuando los datos fluyen en un solo sentido, del servidor al cliente: HTTP plano, reconexión automática, funciona a través de proxies comunes. Los WebSockets ganan su complejidad extra cuando el cliente también debe enviar en tiempo real — chat, juegos, edición colaborativa.
¿SSE es más rápido que el polling?
En latencia de entrega, decisivamente: los eventos llegan cuando suceden, mientras que el polling promedia la mitad del intervalo más un viaje de ida y vuelta. También es más barato — una conexión sostenida en lugar de un desfile de ciclos request/response casi siempre vacíos, cada uno pagando el overhead completo de headers HTTP.
¿Cuándo debería usar long polling?
Como fallback, no como primera opción: existe para entornos donde las conexiones persistentes fallan — intermediarios legacy, proxies que quitan los upgrades de WebSocket o almacenan los streams en buffer. El servidor retiene cada solicitud abierta hasta que llegan datos, lo que aproxima el push al costo de churn de reconexiones y overhead de headers por solicitud.
¿Cuántas conexiones SSE puede abrir un navegador?
Sobre HTTP/1.1, seis por origen — y el límite se comparte entre pestañas, un tropiezo documentado donde un dashboard abierto en siete pestañas se queda sin conexiones en silencio. Sobre HTTP/2 la restricción se muda a los streams concurrentes multiplexados en una conexión (por defecto alrededor de cien), lo que en la práctica retira el problema.
¿SSE se reconecta automáticamente?
Sí — es la característica que más lo distingue de los WebSockets crudos. EventSource reintenta por su cuenta las conexiones caídas, respeta un intervalo de retry fijado por el servidor y envía el último ID de evento recibido en un header Last-Event-ID para que el servidor pueda reanudar el stream sin huecos. La reconexión de WebSocket es código que tú escribes.
¿SSE puede enviar datos binarios?
No — el stream es texto UTF-8 por especificación; los payloads binarios deben codificarse, con un overhead de tamaño de más o menos un tercio. Los WebSockets transportan frames binarios de forma nativa, lo que importa para audio, protocol buffers y cualquier cosa que ya sea compacta.
¿Qué usan las apps de chat con AI para transmitir respuestas?
Server-Sent Events, casi universalmente: la generación token a token es texto en streaming unidireccional, que es precisamente la forma de SSE — HTTP plano de salida, sin negociación de upgrade, reanudación automática. Los WebSockets aparecen en productos de AI solo cuando el cliente debe interrumpir o hablar a mitad del stream, como en las interfaces de voz.
¿Cuál es el orden de fallback para funcionalidades en tiempo real?
Detecta capacidades y degrada: WebSocket donde la ruta lo soporta, SSE donde solo se necesita push del servidor o los upgrades fallan, long polling como mínimo común denominador. Las bibliotecas de tiempo real maduras negocian esta escalera automáticamente — una razón por la que los sockets crudos rara vez se usan pelados en producción.