WebSockets, BroadcastChannel y Server-Sent Events en JavaScript: comunicación en tiempo real

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.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP