Las aplicaciones modernas cada vez necesitan más comunicación en tiempo real: chats, notificaciones en vivo, sincronización entre pestañas, dashboards que se actualizan solos. JavaScript ofrece tres mecanismos nativos para esto: WebSocket para comunicación bidireccional, Server-Sent Events para streaming unidireccional del servidor, y BroadcastChannel para sincronización entre pestañas del mismo origen.
WebSocket: comunicación bidireccional
WebSocket establece una conexión persistente entre el cliente y el servidor que permite enviar y recibir mensajes en cualquier momento y desde cualquiera de los dos extremos. Es la elección para chats, juegos en línea y cualquier escenario donde el servidor necesite iniciar la comunicación.
class ChatWS {
#ws = null;
#reconexiones = 0;
#maxReconexiones = 5;
conectar(url) {
this.#ws = new WebSocket(url);
this.#ws.onopen = () => {
console.log('Conectado');
this.#reconexiones = 0;
};
this.#ws.onmessage = (e) => {
const mensaje = JSON.parse(e.data);
this.#procesarMensaje(mensaje);
};
this.#ws.onclose = (e) => {
console.log(`Cerrado: ${e.code} ${e.reason}`);
if (this.#reconexiones < this.#maxReconexiones) {
const delay = Math.min(1000 * 2 ** this.#reconexiones, 30000);
setTimeout(() => this.conectar(url), delay); // backoff exponencial
this.#reconexiones++;
}
};
this.#ws.onerror = (err) => console.error('Error WS:', err);
}
enviar(tipo, datos) {
if (this.#ws?.readyState === WebSocket.OPEN) {
this.#ws.send(JSON.stringify({ tipo, datos, ts: Date.now() }));
}
}
desconectar() {
this.#ws?.close(1000, 'Cierre normal');
}
#procesarMensaje(msg) {
document.dispatchEvent(new CustomEvent(`ws:${msg.tipo}`, { detail: msg.datos }));
}
}
const chat = new ChatWS();
chat.conectar('wss://ejemplo.com/chat');
chat.enviar('mensaje', { texto: 'Hola', sala: 'general' });
document.addEventListener('ws:mensaje-nuevo', (e) => {
agregarMensajeAlChat(e.detail);
});
Server-Sent Events: streaming del servidor
EventSource establece una conexión HTTP persistente y recibe un flujo de eventos del servidor en texto plano. Es más simple que WebSocket cuando solo el servidor necesita enviar datos, y el navegador gestiona la reconexión automáticamente.
// Cliente
const sse = new EventSource('/api/notificaciones?userId=42');
// Evento por defecto (message)
sse.onmessage = (e) => {
const datos = JSON.parse(e.data);
mostrarNotificacion(datos);
};
// Eventos con nombre personalizados
sse.addEventListener('precio-actualizado', (e) => {
actualizarPrecio(JSON.parse(e.data));
});
sse.addEventListener('alerta-stock', (e) => {
mostrarAlerta(JSON.parse(e.data));
});
sse.onerror = () => {
console.log('SSE error, intentando reconectar...');
// El navegador reconecta automáticamente
};
// Desconectar
// sse.close();
// Servidor (Node.js / Express)
app.get('/api/notificaciones', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const enviar = (evento, datos, id) => {
if (id) res.write(`id: ${id}n`);
res.write(`event: ${evento}n`);
res.write(`data: ${JSON.stringify(datos)}nn`);
};
const intervalo = setInterval(() => {
enviar('precio-actualizado', { simbolo: 'EUR/USD', precio: 1.0842 }, Date.now());
}, 5000);
req.on('close', () => clearInterval(intervalo));
});
BroadcastChannel: sincronización entre pestañas
BroadcastChannel permite que diferentes contextos del mismo origen (pestañas, iframes, workers) se comuniquen directamente sin pasar por el servidor. Útil para sincronizar el estado de sesión, cierre de sesión global o preferencias en tiempo real:
// En todas las pestañas
const canal = new BroadcastChannel('mi-app-canal');
// Escuchar mensajes de otras pestañas
canal.onmessage = (e) => {
if (e.data.tipo === 'cierre-sesion') {
limpiarSesionLocal();
window.location.href = '/login';
}
if (e.data.tipo === 'tema-cambiado') {
aplicarTema(e.data.tema);
}
};
// Enviar a todas las demás pestañas
function cerrarSesionGlobal() {
canal.postMessage({ tipo: 'cierre-sesion', ts: Date.now() });
limpiarSesionLocal();
window.location.href = '/login';
}
function cambiarTema(nuevoTema) {
localStorage.setItem('tema', nuevoTema);
canal.postMessage({ tipo: 'tema-cambiado', tema: nuevoTema });
aplicarTema(nuevoTema);
}
// Cerrar el canal cuando ya no se necesite
// canal.close();
Cuándo usar cada uno
WebSocket es la elección cuando el cliente también necesita enviar datos al servidor frecuentemente (chats, juegos, colaboración en tiempo real). Server-Sent Events es más simple y eficiente cuando el flujo va del servidor al cliente (feeds de noticias, dashboards de métricas, notificaciones push en web). BroadcastChannel no usa red: es exclusivamente para comunicación entre contextos del navegador del mismo usuario y mismo origen, sin latencia y sin carga en el servidor.
