RxJS!

Featured image

RxJS

Buenooo retomando un poco lo que venimos viendo de programación reactiva con RXJS, veamos en detalle algunos operadores para empezar a conocerlos y comprenderlos para empezar a aplicarlos. Recordando el concepto de stream, como colección de datos/eventos futuros.

Repasando brevemente el post anterior, vimos que podemos aplicar distintos operadores para transformar/manipular nuestro streaming, y estan clasificados por categoria. Son un monton, asi que vamos a ver algunosss

Operadores de creación

Create

Creamos un obs que emite hola y amigos cuando te suscribis con create:

    import  { `Observable` }  from  'rxjs';
    const hello = Observable.create((observer)  =>  {
       observer.next('Hola');
       observer.next('Amigos');
    });
    const subscribe = hello.subscribe(val  => console.log(val));

From

Transformamos una colección/array o una promesa en un observable con from:

    import  { `from` }  from  'rxjs';
    const arraySource =  from([1,  2,  3,  4]);

fromEvent

Convertimos cualquier tipo de evento, ejemplo mouse move, en un observable con fromEvent:

    import  { fromEvent }  from  'rxjs';
    const source = fromEvent(document, 'mousemove');
    source.subscribe(e => console.log(e.clientX + ', ' + e.clientY));

Operadores de filtrado

Con estos operadores vamos a filtrar (siii al igual que el filter del array), para aceptar o rechazar ciertos valores según los criterios de filtrado.

Filter

Solo emite valores si cumple la condición dada.

    import { from } from  'rxjs';
    import { filter } from  'rxjs/operators';
    const numbers =  from([2, 30, 22, 5, 60])
    numbers
      .pipe(filter(n => n >  10))
      .subscribe(n => console.log(n))

Nota mental: Cada vez que vayamos a aplicar operadores, escribimos .pipe(), para indicar que se viene la cadeeeena de operadores que deseamos aplicar, si, infinitos sin limitesssss enter image description here

Operadores de transformación

Map

Transforma los elementos emitidos por un observable, aplicando una función a cada uno, en este caso multiplicamos por 10 cada uno de los elementos:

    import { from } from  'rxjs';
    import { map } from  'rxjs/operators';
    const numbers =  from([1, 2, 3, 4])
    numbers
        .pipe(map(x =>  10  * x))
        .subscribe(n => console.log(n))

Reduce

Map, reduce, todo igual que en los Arrays de JS. Aplicamos una función a un acumulador y a cada valor para reducirlo a uno solo.

    import { from } from  'rxjs';
    import { reduce } from  'rxjs/operators';
    const numbers =  from([1, 2, 3, 4])
    numbers
        .pipe(reduce((x, y) => x + y))
        .subscribe(n => console.log(n))

Higher-Order Observables

Y aca empezamos a divertirnos un poco! La magia de los operadores entra en juego cuando empezamos a comprender sus diferencias y el control de los inner/outer observables. qui lo quiiiiiiiiiiii?? enter image description here

Supongamos que tenemos un observable con 2 numeros:

    of(3, 7) //no te olvides el import del of from rxjs :P

Y esos numeros en realidad son los IDs que necesitamos para recuperar datos de un producto:

    of(3, 7)
     .pipe(map(id => this.http.get<Supplier>(´${this.url}/${id}´)))
     .subscribe();

Entonces el primer observable de numeros es el outer Observable. Cada vez que uno de estos numeros es emitido, se mapea a una request que devuelve otro observable. Ahora si este es el inner Observable. Asi que si, asi tenemos observables emitiendo observables!!

Ahora lo primero que pensariamos es, para poder suscribirnos al “inner” y consologuear los datos del producto, tendriamos suscribe anidados (Oh no!), porque recuerden que ya tenemos el subscribe del “outer” (arriba), y que sin susbcribe no hay nada (post anterior, los lazy vagos). Entonces como hacemos, subscribe hell?enter image description here

Usamos los operadores magicos, los high-order observables! Que se suscriben y desuscriben de los inner solitos. Como los podemos reconocer? Justamente por tener la palabra “Map” al final de sus nombres ;) Para todos sirve el mismo ejemplo de arriba, solo cambiando map por el operador de cada sección!

concatMap

Este operador, ESPERA a cada inner que se complete ANTES de continuar y procesar el siguiente. Por eso concatena el resultado de los inner en una secuencia.

La idea es usar concatMap cuando queres asegurarte de que cada inner se procesa uno por vez y en orden. Esta técnica es genial para actualizar o borrar datos, asegurando que cada operación se procesa secuencialmente.

mergeMap/flatMap

Con este amiguito la magia sucede que PARALELIZAAAA. Yes! Es un operador de high-order que procesa los inner en paralelo. Luego mergea los resultados de cada uno. Asi que cuando termina con todos los inner, devuelve el resultado del stream. Y no, no respeta el orden como el anterior, el que llega llega, o el que se fue a la villa perdio su silla?

Usa mergeMap para mejor performance (paralelizar) siempre y cuando no te importe el orden.

switchMap

Justamente el switch lo que hace es switchear entre los inner! Siguiendo como siempre el ejemplo de arriba, pero usando switchMap, podemos ver que apenas se emite el primer valor del outer, se suscribe al primer inner. Cuando éste emite el valor, se DESuscribe del anterior y se subscribe al proximo inner. Cada vez que se completa un inner, emite cada resultado al resultado del stream. Cuando todos terminan, el resultado del stream se completa.

Usa switchMap para frenar los inner ANTES de procesar el siguiente. Imaginemos un type ahead o autocomplete, cuando queres frenar el procesamiento cuando el usuario tipea el siguiente caracter.

Bonus!

enter image description here Siiii hay bonusssss, 2!

1- Playground obviamente para jugar y experimentar un poco con estos conceptos https://rxjs-playground.github.io/#/

2- Existe una extension para VSCode por si queres tener a mano el cheatsheet con los marbles (canicas) https://marketplace.visualstudio.com/items?itemName=dzhavat.rxjs-cheatsheet

Referencias aquí y aquí

Adiooosss