I have an initial 2 http requests I need to make. The first will respond with an array of numbers. Only after the first request can I make the second http request which returns void. After the second http request, I want to then use the values from the first http request and iterate through them making additional http requests for each item
How can I achieve this?
What I have tried
Attempt 1
obs1$ = of([1, 2, 3]);
obs2$ = of(void 0);
concat(obs1$, obs2$).subscribe({next: data => {}});
but data here was of type number[] | undefined, I am only interested in number[]
Attempt 2
obs1$ = of([1, 2, 3]);
obs2$ = of(void 0);
obs1$.pipe(
first(),
tap(() =>
obs2$
)
)
.subscribe({
next: data => {
data.forEach(item =>
//http POST with item
);
}
});
It's not the typical use of tap which is more of side effects, setting a value. All the additional http calls in the subscription doesn't look great and makes error handling tricky
We can use switchMap
to switch between the observables in sequence. There are two ways to do this.
Here we use switchMap, to sequentially execute obs1 followed by obs2, then another switchMap, to call the inner observables based on the returned array. We can use forkJoin
to make the API calls happen in parallel. Finally we get the returned observables in the subscribe.
import './style.css';
import { rx, of, map, switchMap, forkJoin } from 'rxjs';
const obs1$ = of([1, 2, 3]);
const obs2$ = of(void 0);
const fakeApi = (num: number) => of(`${num} - api call done`);
obs1$
.pipe(
switchMap((arrayOfNum: Aray<number>) => {
return obs2$.pipe(
switchMap(() => {
const apiCallArr$ = arrayOfNum.map((num: number) => fakeApi(num));
return forkJoin(apiCallArr$);
})
);
})
)
.subscribe({
next: (response) => {
console.log(response);
},
error: () => console.log('some error occoured'),
});
Here it's the same explanation as above, but we use from
to convert the array, to a stream of observables (only by one emitted). Then we use concatMap
to perform API calls on the stream (also preserving the order of the emits). Finally we use toArray
to collect the values and emit, once the array is completed fully.
import './style.css';
import { rx, of, map, from } from 'rxjs';
import { toArray, switchMap, concatMap } from 'rxjs/operators';
const obs1$ = of([1, 2, 3]);
const obs2$ = of(void 0);
const fakeApi = (num: number) => of(`${num} - api call done`);
obs1$
.pipe(
switchMap((arrayOfNum: Aray<number>) => {
return obs2$.pipe(
switchMap(() => {
return from(arrayOfNum).pipe(
concatMap((data: any) => fakeApi(data)),
toArray()
);
})
);
})
)
.subscribe({
next: (response) => {
console.log(response);
},
error: () => console.log('some error occurred'),
});