Implementando map como operador en Observables
Feb 24, 2022
Updated: Jun 24, 2026

Implementando map como operador en Observables

En este post vamos a explorar cómo aumentar el número de operadores que podemos implementar y usar dentro de un Observable. Primero, recordemos cuál es la definición más pura de un Observable, con una función que genera Observables a partir de eventos:

// Define la clase "Observable"
class Observable {
  // Constructor de la clase "Observable" que acepta un argumento "subscribe"
  constructor(subscribe) {
    // Asigna el valor del argumento "subscribe" a la propiedad "_subscribe" del objeto
    this._subscribe = subscribe;
  }
  // Método "subscribe" que acepta un objeto "observer"
  subscribe(observer) {
    // Llama a la función almacenada en la propiedad "_subscribe" y le pasa el objeto "observer"
    return this._subscribe(observer);
  }
  // Define un método estático "fromEvent" que acepta dos argumentos: "domElement" y "eventName"
  static fromEvent(domElement, eventName) {
    // Retorna una nueva instancia de la clase "Observable"
    return new Observable(function subscribe(observer) {
      // Define una función "handler" que acepta un argumento "ev"
      const handler = (ev) => {
        // Llama al método "next" del objeto "observer" y le pasa el argumento "ev"
        observer.next(ev);
      };
      // Añade un event listener al elemento DOM "domElement" que escucha el evento
      // especificado en "eventName" y llama a "handler" cuando el evento ocurre
      domElement.addEventListener(eventName, handler);
      // Retorna un objeto que contiene un método "unsubscribe"
      return {
        // Define el método "unsubscribe"
        unsubscribe() {
          // Elimina el event listener del elemento DOM asociado con "eventName" y "handler"
          domElement.removeEventListener(eventName, handler);
        },
      };
    });
  }
}

Con esto podemos crear Observables que nos devuelven la información del evento de la siguiente manera:

const button = document.getElementById("button");
const clicks = Observable.fromEvent(button, "click");
const clicksSubscription = clicks.subscribe({
  next(e) {
    console.log("Click in the button", e); // "e" contiene toda la información del evento
  }
});

clicksSubscription.unsubscribe(); // De esta manera te puedes desuscribir

Ahora, imaginemos que queremos hacer más operaciones sobre la cadena de datos, como obtener solo algunos datos del evento, o escuchar solo los clicks que se hagan en la parte izquierda del botón. Vamos a implementar la función map sobre Observables.

La función map es una función pura que itera sobre cada elemento de un array y "mapea" cada ítem de un array en otro:

[1, 2, 3].map(elemento => elemento * 2); // Retorna [2, 4, 6]

Para implementar map en Observables, haremos lo siguiente:

class Observable {
  // ...
  map(projection) {
    const self = this;
    return new Observable(function subscribe(observer) {
      let subscription;
      subscription = self.subscribe({
        next(v) {
          let value;
          try {
            value = projection(v);
            observer.next(value);
          }
          catch (e) {
            observer.error(e);
            // Protegemos la limpieza: durante la primera emisión síncrona
            // "subscription" todavía puede no estar asignado.
            if (subscription) subscription.unsubscribe();
          }
        },
        error(e) {
          observer.error(e);
        },
        complete() {
          observer.complete();
        }
      });
      return subscription;
    });
  }
  // ...
}

Un detalle importante de este código: dentro del catch llamamos a subscription.unsubscribe(), pero subscription es la misma variable que estamos asignando con esa llamada a self.subscribe({...}). Si la proyección lanza un error durante la primera emisión síncrona, subscription todavía no estaría asignado y la limpieza fallaría con un segundo error. Por eso lo protegemos con if (subscription). En el ejemplo de fromEvent los clicks son asíncronos, así que rara vez se da el caso, pero es una buena costumbre dejar la limpieza a prueba de fallos.

Y ahora podemos usar map para transformar los datos dentro de nuestro Observable, obteniendo solamente la posición del mouse en el momento en que se hizo click sobre el botón:

const button = document.getElementById("button");
const clicks = Observable.fromEvent(button, "click");
const clicksSubscription = clicks
  .map((ev) => ev.offsetX)
  .subscribe({
    next(offSetX) {
      console.log(offSetX);
    },
  });

Con estos fragmentos tienes la definición completa: la clase Observable con su constructor y subscribe, el método estático fromEvent y el operador map. Te recomiendo copiarlos en un editor en línea como CodePen o CodeSandbox y experimentar con ellos para verlos en acción.

Conclusiones

  • Hemos implementado la función map en Observables, lo que nos permite realizar transformaciones en los datos de eventos de una manera más flexible.
  • map en Observables es similar a su uso en arrays, permitiendo aplicar una función de proyección a cada elemento.
  • Al usar map en Observables, podemos realizar operaciones más avanzadas y personalizadas en la cadena de datos, lo que mejora la calidad y la eficiencia de nuestro código.

Ejercicios para practicar

  1. Implementa la función filter en Observables para permitir filtrar eventos basados en una condición específica.
  2. Crea un Observable que devuelva eventos de teclado y usa map para transformar la salida en objetos que contengan solo la tecla presionada y el momento en que ocurrió el evento.
  3. Combina el uso de map y filter en Observables para procesar eventos de mouse y devolver solo la información relevante de aquellos eventos que cumplan ciertos criterios.

Resumen en 3 puntos

  1. Hemos ampliado nuestra implementación de Observables con el operador map, lo que nos permite transformar los datos de los eventos de forma más flexible y eficiente.
  2. La función map en Observables es similar a su uso en arrays, aplicando una función de proyección a cada elemento de la secuencia.
  3. Con map podemos realizar transformaciones avanzadas y personalizadas en la cadena de datos, lo que nos permite manejar eventos de manera más efectiva en nuestras aplicaciones.

Espero que este post te haya sido útil y te haya ayudado a comprender cómo implementar el operador map en Observables. Si tienes alguna pregunta o comentario, no dudes en dejarlo en la sección de comentarios. Buena suerte en tus proyectos y no olvides seguir aprendiendo y practicando.

Si te gustó este post, no olvides compartirlo con tus amigos y colegas a través de las redes sociales utilizando los enlaces de aquí abajo. ¡Hasta la próxima!

Sebastian Gomez

Sebastian Gomez

Creador de contenido principalmente acerca de tecnología.

Leave a Reply

0 Comments

Advertisements

Related Posts

Categorias