Concatenando observables en RXJS 🧵
Programando, en ocasiones, se hace obligatorio esperar una respuesta para poder continuar con el flujo de la aplicación.
En aplicaciones de backend podemos generar varios hilos para evitar bloquear la aplicación mientras se espera una respuesta, ya sea un cálculo o una petición a otra fuente de datos.
En frontend, para no bloquear la aplicación usamos programación reactiva.
Antes usábamos los callbacks.
No quiero recordar cuando no existían las promesas y todo eran callbacks, la que se liaba cuando querías concatenar varios callback, lo que conocemos como callback hell 👺
A partir del ECMAScript 6 se introdujeron las promesas y se nos facilitó el código asíncrono.
Con las nuevas palabras claves de async y await que añadieron en ECMAScript 2017 se nos proporcionó una manera aún más fácil y legible de hacer programación asíncrona con las promesas.
¿Pero qué ocurre cuando trabajamos con observables?
¿Por qué necesito saber concatenar observables?
Cuando trabajamos con Angular y hacemos peticiones HTTP con su cliente trabajaremos con observables, por eso es necesario saber concatenarlos si no queremos ir anidando observables al estilo del callback hell.
Los observables son un patrón de diseño utilizado en programación reactiva.
Representa una fuente de datos o eventos asincrónicos y permite a los observadores suscribirse a ella para recibir notificaciones cuando ocurran cambios en dichos datos.
Te cuento cómo podemos concatenar varios observables usando operadores de orden superior de RXJS.
Concatenando observables
Te voy a explicar 3 formas de concatenar observables, pero antes puede que te preguntes por qué te podría hacer falta aprender a concatenar observables.
Imagina que necesitas obtener una factura de un cliente haciendo una consulta a una API, pero sólo tienes el número de factura.
La API sólo acepta consultas por Id, pues lo que tendríamos que hacer sería:
- Obtener el Id de la factura pasándole a la API el código de cliente y número de factura
- Una vez que obtengas la respuesta de la primera consulta, con el id de la factura, hacer la petición con ese número de id.
Si te fijas, es obligatorio esperar que se resuelva la primera petición para que el proceso se ejecute satisfactoriamente.
Vamos a ver cómo podemos hacerlo con observables.
Para los ejemplos voy a utilizar la API que nos proporciona de forma gratuita reqres.in
Operador concatAll
El operador concatAll se utiliza para fusionar un observable de observables.
Toma un observable que emite observables como emisiones y los concatena en un solo observable.
Es decir, se suscribe secuencialmente a cada observable interno y emite sus valores en orden.
Solo pasa al siguiente observable interno cuando el anterior ha terminado.
this.http .get<RawData>('https://reqres.in/api/users?page=2') .pipe( map((response) => response.data[0].id), map((id: number) => this.http.get<SingleUser>(`https://reqres.in/api/users/${id}`) ), concatAll() );
Para tipar las respuestas de los observables he usado las siguiente interfaces:
export interface RawData { page: number; per_page: number; total: number; total_pages: number; data: User[]; support: Support; } export interface User { id: number; email: string; first_name: string; last_name: string; avatar: string; } export interface Support { url: string; text: string; } export interface SingleUser { data: User; support: Support; }
Operador concatMap
El operador concatMap se utiliza para mapear los resultados de varios observables en un solo observable de salida.
Mantiene el orden relativo de las emisiones y espera a que cada observable interno se complete antes de pasar al siguiente.
this.http .get<RawData>('https://reqres.in/api/users?page=2') .pipe( concatMap((response) => response.data[0].id), concatMap((id: number) => this.http.get<SingleUser>(`https://reqres.in/api/users/${id}`) ).subscribe((data)=>{ //Aquí lo que quieras hacer con la respuesta del último observable }) );
Operador switchMap
El operador switchMap se utiliza para mapear cada valor de un observable de entrada a un observable interno y, posteriormente, emitir los valores del observable interno.
Si se emiten nuevos valores en el observable de entrada antes de que el observable interno haya terminado, se cancela el observable interno actual y se cambia al nuevo observable.
Aquí es donde radica la característica principal del operador switchMap, en que se cancela el observable actual si se reciben nuevos datos.
Es muy utilizado con las cajas de búsquedas.
Imagina la típica caja de búsqueda de un e-commerce, cuando el usuario escribe se hace la petición a la API, pero si el usuario vuelve a escribir antes de recibir la respuesta de la API se cancelará la primera petición.
this.http .get<RawData>('https://reqres.in/api/users?page=2') .pipe( map((response) => response.data[1].id), switchMap((id: number) => this.http.get<SingleUser>(`https://reqres.in/api/users/${id}`) ) );
Hasta luego 🖖