'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?
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');
},
});
Solution 1:[1]
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');
},
});
Solution 2:[2]
You could just pipe in the 2ndconcatMap 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
Solution 3:[3]
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);
Solution 4:[4]
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');
},
});
Solution 5:[5]
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
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | olivarra1 |
| Solution 2 | ruth |
| Solution 3 | daflodedeing |
| Solution 4 | Shlomi Levi |
| Solution 5 | Picci |
