Tema 14 · Patrón Observer

Patrón Observer y EventEmitter: Comunicación Reactiva entre Objetos

Aprende el patrón que gobierna la reactividad en JavaScript: el Patrón Observer y cómo construir un EventEmitter desacoplado para comunicar objetos.

El Corazón de la Reactividad en JavaScript

En el desarrollo web, los objetos rara vez operan de forma aislada. Cuando ocurre una acción importante (como un usuario haciendo clic en un botón, la llegada de un mensaje por WebSocket o la confirmación de una compra en el backend), múltiples módulos deben reaccionar sin acoplarse rígidamente entre sí.

El Patrón Observer (Observador o Publicador/Suscriptor) resuelve este reto: un objeto emisor notifica automáticamente a una lista de suscriptores cada vez que ocurre un evento de su interés.

La Tríada de un EventEmitter

Método Propósito Ejemplo de Uso
on(evento, callback) Suscribirse: Añade una función escuchadora a la lista de ese evento. emisor.on("compra", enviarEmail);
emit(evento, datos) Publicar: Dispara el evento y ejecuta todos los callbacks suscritos pasándoles la información. emisor.emit("compra", { total: 150 });
off(evento, callback) Desuscribirse: Remueve un callback para evitar fugas de memoria. emisor.off("compra", enviarEmail);

⚡ Construcción de un EventEmitter Reactivo en JavaScript Puro

Ejecuta el código en el Sandbox y observa cómo la tienda notifica a email, inventario y analítica sin acoplamiento:

EventEmitterDemo.js
Solo lectura
// 1. Clase EventEmitter Genérica
class EventEmitter {
    #eventos = new Map();

    // Suscribir un callback a un evento
    on(nombreEvento, callback) {
        if (!this.#eventos.has(nombreEvento)) {
            this.#eventos.set(nombreEvento, new Set());
        }
        this.#eventos.get(nombreEvento).add(callback);
    }

    // Desuscribir
    off(nombreEvento, callback) {
        if (this.#eventos.has(nombreEvento)) {
            this.#eventos.get(nombreEvento).delete(callback);
        }
    }

    // Disparar evento a todos los observadores
    emit(nombreEvento, datos) {
        if (!this.#eventos.has(nombreEvento)) return;

        const observadores = this.#eventos.get(nombreEvento);
        for (const callback of observadores) {
            callback(datos);
        }
    }
}

// 2. Modelo de Tienda que Hereda de EventEmitter
class TiendaOnline extends EventEmitter {
    constructor() {
        super();
        this.pedidos = [];
    }

    crearPedido(cliente, producto, total) {
        const pedido = {
            id: "PED-" + Math.floor(1000 + Math.random() * 9000),
            cliente,
            producto,
            total,
            fecha: new Date().toLocaleTimeString()
        };

        this.pedidos.push(pedido);
        console.log(`\n🛒 [Tienda] Nuevo pedido generado: #${pedido.id}`);

        // Notificar a todos los módulos observadores suscritos:
        this.emit("pedido_creado", pedido);
    }
}

// ==========================================
// OBSERVADORES DESACOPLADOS (Módulos de la App)
// ==========================================
const moduloEmail = (pedido) => {
    console.log(`📧 [Servicio Email] Enviando confirmación de compra a: ${pedido.cliente}`);
};

const moduloAlmacen = (pedido) => {
    console.log(`📦 [Inventario] Descontando 1 unidad de "${pedido.producto}" del stock.`);
};

const moduloFacturacion = (pedido) => {
    console.log(`🧾 [Facturación] Factura generada por un total de $${pedido.total.toFixed(2)} USD.`);
};

// ==========================================
// PRUEBA EN EL SANDBOX
// ==========================================
const amazonClone = new TiendaOnline();

// Suscripción de observadores al evento "pedido_creado"
amazonClone.on("pedido_creado", moduloEmail);
amazonClone.on("pedido_creado", moduloAlmacen);
amazonClone.on("pedido_creado", moduloFacturacion);

// Disparo de eventos: Cada observador reacciona de forma independiente
amazonClone.crearPedido("Carlos Mendoza", "MacBook Pro M3", 2499.00);
amazonClone.crearPedido("Valeria Torres", "Mouse MX Master 3S", 99.90);
Pruébalo tú mismo » Se abre en una nueva pestaña con ejecución en vivo
🧠
Mini-Quiz de Retención
Comprueba tu comprensión antes de pasar a la siguiente lección
1 Pregunta
Pregunta 1
¿Cuál es la función del método emit(nombreEvento, datos) en el patrón Observer / EventEmitter?
🦉
¿Tienes dudas con este tema?
Sócrates AI puede darte analogías y explicarte este código paso a paso.

🚀 ¿Quieres dominar Manual de Programación Orientada a Objetos en JavaScript (POO) y crear proyectos reales?

Aprende arquitectura limpia, desarrollo web moderno, buenas prácticas y despliegue en producción con nuestro Bootcamp Fullstack Developer con mentorías en vivo.

🏛️ Autoría y Propiedad Intelectual: Obra educativa original de David Amador para Kilopolis Academia (Solucionáticos), estructurada sobre las especificaciones y estándares abiertos del W3C, el estándar vivo de WHATWG y la documentación oficial de código abierto. © Todos los derechos reservados.