RxJS es una librería muy útil de Javascript, que te ayuda a gestionar secuencias de eventos.
Imagina una caja de búsqueda: Necesitas gestionar eventos del input, estrategias de debounce, llamadas a servidor… Ese sería un escenario ideal para RxJS.

Si nunca has oído hablar de ella, te recomiendo que le des un vistazo a mi introducción a RxJS y a mis fundamentos de RxJS. Te puede parecer muy teórico, pero eso va a cambiar a partir de YA… En este artículo voy a explicarte como se utiliza RxJS de forma práctica, a través de código.

Elementos básicos de RxJS

Déjame repasar la jerga. Los conceptos principales de RxJS son:

  • Observable: El flujo de datos, una colección de eventos que se pueden emitir en algún momento.

  • Observer: Un objeto que escucha el flujo de datos y puede actuar sobre los valores que éste emite.

  • Subscription: Representa la ejecución de un observable y permite cancelarla.

  • Operador: Función para manipular los eventos siguiendo los principios de la programación funcional.

Creando un Observable

Pongamos que quiero emitir el texto «Hello world», letra a letra.

Para eso están los Observables, ¿no?

Una de las formas más simples de crear un Observable es la función from: recibe un array o una cadena de texto y devuelve un flujo con su contenido, item a item.

import { from } from 'rxjs'; 

//create the "Hello world" observable
const myObs = from("Hello world");

Genial, myObs está listo para emitir el flujo de datos «H,e,l,l,o, ,w,o,r,l,d». Este flujo contiene una letra por evento, incluido el espacio, obviamente.

Operadores

Pongamos ahora que no quiero espacios en la salida del flujo.
No hay problema: puedo usar el operador filter de RxJS para eliminar eventos que coincidan con un espacio.

import { from } from 'rxjs'; 

//create the "Hello world" observable
const myObs = from("Hello world");

//apply the filter operator
myObs.pipe(
  filter(char => char != ' ')
)

El mecanismo para añadir operadores (pipe) te lo explicaré en breve.
De momento, fíjate que al operador filter le pasas una función que recibe como entrada el evento a filtrar. filter solo deja pasar eventos cuando la función de filtrado devuelve true.

Vale, con esto deberías tener ya la secuencia «H,e,l,l,o,w,o,r,l,d», sin espacios.
Pero hay un problema. En realidad no se está ejecutando nada.

Suscripciones

Un Observable no se ejecuta mientras no tenga un Observer suscrito.

Grábate esto a fuego. Siempre.

Si quiero que se emitan los datos y se ejecute el operador de filtrado, necesito una Suscripción de un Observer.

import { from } from 'rxjs';
import { filter }  from  'rxjs/operators'; 

//create the "Hello world" observable
const myObs = from("Hello world");

//apply the filter operator
const filteredObs = myObs.pipe(
  filter(char => char != ' ')
);

//subscribe to the filtered observable
const subscription = filteredObs.subscribe(char => console.log(char));

Ahora si, mi salida por consola es la secuencia «H,e,l,l,o,w,o,r,l,d».

Las Suscripciones sirven, también, para cancelar el flujo de ejecución. Si yo quisiera interrumpir el flujo tras la letra «e», podría usar la suscripción así:

const subscription = filteredObs.subscribe(char => {
  console.log(char);
  if(char == 'e')
    subscription.unsubscribe();
});

Y de este modo, solo se emitiría la secuencia «H,e».

OFERTA
Curso

RxJS Nivel PRO

Entiende qué es y cómo usar RxJS: Aprende infinidad de Operadores RxJS y Domina laProgramación Reactiva.
Idioma: Español
23,9 €125 €

 

Observers

Por cierto, la función char => console.log(char) de la suscripción, es justamente el Observer (o mejor dicho, su función next). Recuerda que te he definido el Observer como un objeto que recibe el flujo de datos y puede usar los valores recibidos. En este caso, simplemente los muestro por consola.

En realidad, un Observer puede ser más complejo. Su interfaz define 3 métodos:

  • next: que es el que recibe y usa los datos
  • error: para capturar los errores que pueda emitir un Observable
  • complete: para cerrar la suscripción (y por tanto el flujo de datos)

Podría escribir el código anterior con un Observer completo, así:

import { from } from 'rxjs';
import { filter }  from  'rxjs/operators'; 

//create the "Hello world" observable
const myObs = from("Hello world");

//apply the filter operator
const filteredObs = myObs.pipe(
  filter(char => char != ' ')
);

const observer = {
  next: (evt) => console.log(evt),
  error: (err) => console.error(err),
  complete: () => console.log("completed")
}

//subscribe to the filtered observable
const subscription = filteredObs.subscribe(observer);

Encadenado de operadores (la función pipe)

RxJS va de manipular flujos de datos. Y para eso tiene los operadores, como has visto.

Los operadores son funciones puras con una aproximación funcional:

  • No modifican el objeto de entrada, sino que devuelven un objeto nuevo
  • El resultado solo depende de la entrada y de la propia función
  • Y se pueden encadenar

Esto no debería sonarte extraño. El mismo objeto Array de Javascript tiene métodos que son funciones puras. Aquí tienes un ejemplo:

let array =  new  Array(1,2,3,4,5);
array.filter(x => x>3).map(x => x*2)
//output: [8,10]

En versiones anteriores a RxJS 5.5, los operadores de RxJS se aplicaban del mismo modo. Pero la forma de importarlos por separado era muy incómoda. Para solucionarlo, añadieron el método pipe al objeto Observable.

El método pipe te permite aplicar varios operadores sobre el flujo de datos de forma secuencial.

Por seguir con el ejemplo del «Hello world», imagina que además, quiero tener todas las letras en mayúsculas. Puedo conseguirlo con el operador map de RxJS.

import { from } from 'rxjs'; 
import { filter, map }  from  'rxjs/operators';

//create the "Hello world" observable
const myObs = from("Hello world");

const filteredObs = myObs.pipe(
  filter(char => char != ' '),
  map(char => char.toUpperCase())
);

//subscribe to the filtered observable
const subscription = filteredObs.subscribe(char => console.log(char));
//output: H E L L O W O R L D

Como ves, pipe recibe un array de operadores, de modo que cada operador va modificando el flujo de datos. En este ejemplo, el operador map no recibiría nunca un evento con el carácter de espacio (» «), porque el operador filter ya se habría encargado de eliminar ese tipo de datos del flujo.

Ojo, no debes confundir el operador map con Array.map. Son ideas similares, pero el operador map trabaja sobre un flujo de eventos, mientras que Array.map trabaja sobre los elementos de un array.

Es importante que entiendas que en este ejemplo, la salida son 10 eventos separados. Uno con la letra «H», el siguiente con la «E», etc. En este caso son eventos consecutivos, pero podrían estar más espaciados en el tiempo.

¿Necesito una librería para gestionar flujos de datos?

Dímelo tu cuando acabe este ejemplo. Para que veas una pincelada de lo que es capaz RxJS, voy a modificar el código de modo que cada letra tenga un retraso de 1s con la anterior.

Te adelanto los nuevos elementos que voy a introducir:

  • función of: Devuelve un Observable que emite un valor concreto
  • operador delay: Añade un retraso al inicio del flujo de datos
  • operador concatMap: Genera un nuevo observable a partir del evento recibido, se suscribe y emite sus valores hasta que termine. Luego, repite la operación con el siguiente evento que reciba.
import { from, of} from 'rxjs'; 
import { filter, map, delay, concatMap } from 'rxjs/operators';

const myObs = from("Hello world");

const filteredObs = myObs.pipe(
  //here comes the magic 🙂
  concatMap(char => of(char).pipe(delay(1000))),
  filter(char => char != ' '),
  map(char => char.toUpperCase()),
);

const subscription = filteredObs.subscribe(char => console.log(char));

Fíjate lo que consigo con solo una línea: Al recibir un evento (una letra en este caso), creo un nuevo Observable que emite únicamente esa letra, pero con un retraso de 1s. concatMap se suscribe (para que se ejecute) y cuando termina (al cabo de 1s), repite la operación con el siguiente evento (la siguiente letra).

Es decir, con una sola línea he conseguido que ahora las letras se emitan con un espacio de 1s entre ellas.

A continuación puedes ver una animación que muestra como cada elemento de la pipe manipula el flujo de datos en este ejemplo:

Resumen

Te he mostrado un ejemplo muy simple, que debería ayudarte a hacerte una imagen global de lo que es RxJS ¡Pero esto es solo la punta del Iceberg! En el próximo artículo te enseñaré algunos de los operadores más útiles que tiene RxJS.

Si no puedes esperar a saber más, te recomiendo que le des un vistazo a mi curso RxJS Nivel PRO, con un espectacular rating 4.9 sobre 5!!

Y como siempre, si te ha gustado este artículo… No te cortes, déjame un comentario y ayúdame a compartirlo 😉

OFERTA
Curso

RxJS Nivel PRO

Entiende qué es y cómo usar RxJS: Aprende infinidad de Operadores RxJS y Domina laProgramación Reactiva.
Idioma: Español
23,9 €125 €