Cómo encadenar correctamente concatMaps en Angular RxJS

CorePress2024-01-25  14

    this.route.params.pipe(
      concatMap((param) => {
        const ein = param['nonprofit-id'];
        return this.nonprofitService.getNonprofit(ein).pipe(
          concatMap((nonprofit) => {
            this.nonprofit = nonprofit;
            const ratingID = nonprofit.currentRating?.ratingID ? nonprofit.currentRating.ratingID : -1;
            return this.nonprofitService.getRatingForNonprofit(ein, ratingID).pipe(
              concatMap((nonprofitRating) => {
                this.nonprofitRating = nonprofitRating;
                const causeID = this.nonprofit?.cause?.causeID ? this.nonprofit?.cause?.causeID : 0;
                return this.nonprofitService.getSimilarNonprofits(causeID);
              })
            );
          })
        );
      })
    ).subscribe((response) => {
      this.similarNonprofits = response;
    });

Me gustaría saber si lo anterior es la forma correcta de encadenar concatMaps en Angular RxJS. Las otras dos convocatorias se basan en la iniciativa "sin fines de lucro". en cuestión se recupera para que su objeto pueda ser devuelto. La calificación y las organizaciones sin fines de lucro similares se pueden recuperar al mismo tiempo, así que pensé que habría alguna forma de hacerlo sin anidar esos concatMaps entre sí.



------------------------------------

Si necesita usar todos los valores anteriores, generalmente termina anidando más y más cadenas, pero en este caso ya parece demasiado complicado, por lo que puede mapear() cada Observable interno en una matriz (u objeto) como el siguiente:

this.route.params.pipe(
  concatMap((param) => {
    const ein = param['nonprofit-id'];
    return this.nonprofitService.getNonprofit(ein).pipe(
      map(nonprofit => [ein, nonprofit]),
    );
  ),
  concatMap(([ein, nonprofit]) => {
    const ratingID = ...;
    this.nonprofitService.getRatingForNonprofit(ein, ratingID).pipe(
      map(nonprofitRating => [nonprofitRating, ein, nonprofit]),
    );
  }),
  concatMap(([nonprofitRating, ein, nonprofit]) => {
    const causeID = ...;
    return this.nonprofitService.getSimilarNonprofits(causeID).pipe(
      map(similar => [similar, nonprofitRating, ein, nonprofit]),
    );
  }
).subscribe(([similar, nonprofitRating, ein, nonprofit]) => { ... });

Obviamente, no es necesario desenvolver cada matriz pasada como parámetro a concatMaps consecutivos, pero espero que entiendas el punto.

2

Ahhh, entonces si lo hago uno al lado del otro como parámetros en la función de tubería, el valor que se pasa como parámetro para la función de flecha es justo lo que se asigna en el retorno de el concatMap anterior?

- Andrés

27/03/2021 a las 22:09

Sí, cada matriz es una única notificación siguiente que será procesada por el siguiente concatMap

- Martín

27/03/2021 a las 22:29



------------------------------------

Continuando con la respuesta de @martin, sugeriría usar forkJoin para combinar los dos últimos concatMaps en una sola solicitud, ya que no dependen uno del otro. Esto también activaría las solicitudes en paralelo y podría ayudar a mejorar el rendimiento (aunque sea minúsculo).

También reemplacé el operador ternario con el operador coalescente nulo ??.

this.route.params.pipe(
  concatMap((param: any) => {
    const ein = param['nonprofit-id'];
    return this.nonprofitService.getNonprofit(ein).pipe(
      map((nonprofit) => ([ein, nonprofit]))
    );
  }),
  concatMap(([ein, nonprofit]) => 
    forkJoin([
      this.nonprofitService.getRatingForNonprofit(ein, (nonprofit.currentRating?.ratingID ?? -1)),
      this.nonprofitService.getSimilarNonprofits(ein, (this.nonprofit?.cause?.causeID ?? 0))
    ]).pipe(
      map(([nonprofitRating, similarNonprofits]) => ([
        nonprofit,
        nonprofitRating,
        similarNonprofits
      ]))
    )
  )
).subscribe({
  next: ([nonprofit, nonprofitRating, similarNonprofits]) => {
    this.nonprofit = nonprofit;
    this.nonprofitRating = nonprofitRating;
    this.similarNonprofits = similarNonprofits;
  },
  error: (error: any) => {
    // handle error
  }
});

También puedes definir un objeto con las propiedades para evitar el uso de sintaxis desestructurante con múltiples elementos.

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare