简体   繁体   中英

How to get results from functions that use concatMap in rxjs?

I calling to two functions: fn1 and fn2 . I use concatMap to invoke other after the other.

I don't use exhustMap and switchMap because they lead to nested "callback-hell".

  exhaustMap(() => 
     fn1().pipe(
       switchMap(() => fn2()))))

The one problem is how to get the results of fn1 and fn2 into next function that happens after fn2 is invoked?

stackblitz.com

import { of } from 'rxjs';
import { concatMap, tap } from 'rxjs/operators';
console.clear();

const fn1 = () => {
  console.log('in fn1');
  return of('fn1');
};

const fn2 = () => {
  console.log('in fn2');
  return of('fn2');
};

of(1)
  .pipe(
    concatMap(() => fn1()),
    concatMap(() => fn2()),
    tap({
      next: (a) => {
        console.log({ a }); /// <---- here I want to get fn1, fn2 from the functions.
        console.log('in tap!!!');
      },
      error: () => {
        console.log('in tap error!!!');
      },
      complete: () => {
        console.log('in tap complete');
      },
    })
  )
  
  .subscribe({
    next: (r) => {
      console.log({ r });
    },
    error: (e) => {
      console.log({ e });
    },
    complete: () => {
      console.log('complete');
    },
  });

I don't think there's an existing operator that could avoid the "operators-hell", but maybe you can build one that would?

Something like

import { from, ObservableInput, Observable } from "rxjs";
import { concatMap, map } from 'rxjs/operators';

const appendMap =
  <T, R extends ObservableInput<any>>(mapFn: (value: T) => R) =>
  (source$: Observable<T>) =>
    source$.pipe(
      concatMap((value) =>
        from(mapFn(value)).pipe(map((result) => [value, result] as const))
      )
    );

So now you can use it and avoid "operators-hell":

import { of } from 'rxjs';
import { tap } from 'rxjs/operators';

const fn1 = () => of('fn1');

const fn2 = () => of('fn2');

of(1)
  .pipe(
    appendMap(() => fn1()),
    map(([_, fn1Result]) => fn1Result),
    appendMap(() => fn2()),
    tap({
      next: ([fn1Result, fn2Result]) => {
        console.log({ fn1Result, fn2Result });
        console.log('in tap!!!');
      },
      error: () => {
        console.log('in tap error!!!');
      },
      complete: () => {
        console.log('in tap complete');
      },
    })
  )
  .subscribe({
    next: (r) => {
      console.log({ r });
    },
    error: (e) => {
      console.log({ e });
    },
    complete: () => {
      console.log('complete');
    },
  });

You could just pipe in the 2 nd concatMap to the fn1() and use map to emit both the values.

of(1)
  .pipe(
    concatMap(() => 
      fn1().pipe(
        concatMap((fn1Value) => fn2().pipe(
          map((fn2Value) => ({
            fn1: fn1Value,
            fn2: fn2Value
          }))
        ))
      )
    )

I've adjusted your Stackblitz

The closest I can think of is here: Stackblitz

const fn1$ = of(1);
const fn2$ = of(2);

fn1$.pipe(
    concatWith(fn2$)
  ).pipe(
      bufferCount(2)
).subscribe(console.log);

use zip :

stackblitz

import { of, zip } from 'rxjs';
import { concatMap, tap } from 'rxjs/operators';
console.clear();

const fn1$ = of('fn1');
const fn2$ = of('fn2');

zip(fn1$, fn2$)
  .pipe(
    tap({
      next: (a) => {
        console.log({ a }); // a === [fn1,fn2]
        console.log('in tap!!!');
      },
      error: () => {
        console.log('in tap error!!!');
      },
      complete: () => {
        console.log('in tap complete');
      },
    })
  )
  .subscribe({
    next: (r) => {
      console.log({ r });
    },
    error: (e) => {
      console.log({ e });
    },
    complete: () => {
      console.log('complete');
    },
  });

I would do something like this

of(1)
  .pipe(
    concatMap(() => fn1()),
    concatMap((resp_1) => fn2().pipe(
      map(resp_2 => ({resp_1, resp_2})
    )),
    tap({
      next: ({resp_1, resp_2}) => {
        console.log(resp_1, resp_2); 
        console.log('in tap!!!');
      },
      error: () => {
        console.log('in tap error!!!');
      },
      complete: () => {
        console.log('in tap complete');
      },
    })
  )

A bit of operators hell but maybe not too much

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM