El patrón Observer y el funcionamiento de los Observables

Fundamento del patrón Observer

El patrón de diseño Observer describe un comportamiento de tipo publicador-suscribidor: un objeto (conocido como Subject u Observable) mantiene una lista de dependencias llamadas Observers o suscriptores y les notifica de forma automática cualquier cambio de estado o emisión de datos.

Observables vs Promesas (Promises)

Una Promesa representa un valor único que se resolverá en el futuro y es eager: se ejecuta de inmediato al crearse. Un Observable, en cambio, representa un flujo de múltiples valores a lo largo del tiempo y es lazy: no emite nada hasta que alguien se suscribe explícitamente.

Ciclo de vida de un Observable

Un Observable gestiona tres canales de comunicación con sus suscriptores:

  1. next(): Emite un nuevo valor o paquete de datos hacia el flujo.
  2. error(): Notifica que ocurrió una excepción e interrumpe definitivamente la ejecución del flujo.
  3. complete(): Indica que el flujo ha terminado de emitir datos y cierra el canal de forma limpia.

Implementación conceptual básica en JavaScript puro

class Observable {
  constructor(setupFunction) {
    this.setupFunction = setupFunction;
  }

  subscribe(observer) {
    // El observable se ejecuta unicamente cuando alguien se suscribe (Lazy)
    return this.setupFunction(observer);
  }
}

// Uso del Observable
const streamNumerico = new Observable((observer) => {
  let contador = 0;
  const intervalo = setInterval(() => {
    observer.next(contador++);
    if (contador > 3) {
      observer.complete();
      clearInterval(intervalo);
    }
  }, 1000);

  // Funcion de limpieza (Teardown logic)
  return () => clearInterval(intervalo);
});

Gestión de memoria y desuscripción

Un error habitual con flujos asíncronos es olvidar cancelar las suscripciones (unsubscribe). Eso genera fugas de memoria severas (memory leaks), porque mantenemos referencias activas a componentes que ya no están visibles en el DOM.

Preguntas clave

  1. ¿Cuál es la diferencia de comportamiento en la ejecución (eager vs lazy) entre una Promesa nativa y un Observable?
  2. ¿Por qué la gestión de funciones de limpieza (teardown logic) dentro de las suscripciones es crítica para prevenir fugas de memoria en aplicaciones complejas?

© Arturo Juárez Monroy