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:
// 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);
Mini-Quiz de Retención
Comprueba tu comprensión antes de pasar a la siguiente lección¿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.