0

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');
    },
  });

2
  • Is it possible to run the functions in parallel? Then you can use forkJoin operator to call them like forkJoin([fn1(), fn2()] instead of concatMap. The result array contains the result of both functions then. Commented May 10, 2022 at 10:55
  • no, they need to be execute one after the other. if the first failed then the second should not execute. Commented May 10, 2022 at 10:59

5 Answers 5

2

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');
    },
  });
Sign up to request clarification or add additional context in comments.

Comments

0

You could just pipe in the 2nd 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

2 Comments

mmmmm, can I avoid "operators-hell" structure?
@JonSud: It depends, if the observables fn1() and fn2() are independent of each other, you wouldn't even need the concatMap. You could use something forkJoin or combineLatest. They would emit all the emissions as an array. Please see here for more info.
0

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);

Comments

0

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');
    },
  });

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.