I would like to know what is the best way using the RxJS library to execute 3 http requests that depends from the previous result.
Let's imagine that I've 3 services in my Angular application and each of them have a function get(id: number) use to subscribe an observable of the request entity.
I need to call sequencing the first service to get an entity which contains an identifier required for the next call by using the second service which also contains an identifier required for the next call using the third service.
const firstEntityId = 1;
this.firstService.get(firstEntityId)
.subscribe((firstEntity: FirstEntity) => {
this.firstEntity = firstEntity;
this.secondService.get(firstEntity.secondEntityId)
.subscribe((secondEntity: SecondEntity) => {
this.secondEntity = secondEntity;
this.thirdService.get(secondEntity.thirdEntityId)
.subscribe((thirdEntity: ThirdEntity) => {
this.thirdEntity = thirdEntity;
});
});
});
const firstEntityId = 1;
this.getFirstSecondThird(firstEntityId)
.subscribe(([firstEntity, secondEntity, thirdEntity]: [FirstEntity, SecondEntity, ThirdEntity]) => {
this.firstEntity = firstEntity;
this.secondEntity = secondEntity;
this.thirdEntity = thirdEntity;
});
getFirstSecondThird(id: number): Observable<[FirstEntity, SecondEntity, ThirdEntity]> {
return this.firstService.get(id).pipe(
switchMap((firstEntity: FirstEntity) => forkJoin(
of(firstEntity),
this.secondService.get(firstEntity.secondEntityId)
)),
switchMap(([firstEntity, secondEntity]: [FirstEntity, SecondEntity]) => forkJoin(
of(firstEntity),
of(secondEntity),
this.thirdService.get(secondEntity.thirdEntityId)
))
);
}
In this case, does the method using stream is the fastest one ?
Is there an other way to write my function getFirstSecondThird instead of using switchMap and forkJoin methods ?
(I've seen combineLatest but I didn't found how to pass a parameter from the previous result)
Maybe use map instead subscribe in method 1?
Note, you need to return at all nested levels. In the example I have removed the brackets so the return is implied.
getFirstSecondThird(id: number): Observable<[FirstEntity, SecondEntity, ThirdEntity]> {
return this.firstService.get(id).pipe(
mergeMap((first: FirstEntity) =>
this.secondService.get(first.secondEntityId).pipe(
mergeMap((second: SecondEntity) =>
this.thirdService.get(second.thirdEntityId).pipe(
map((third: ThirdEntity) => [first, second, third])
)
)
)
)
)
}
Here is a test snippet,
console.clear()
const { interval, of, fromEvent } = rxjs;
const { expand, take, map, mergeMap, tap, throttleTime } = rxjs.operators;
const firstService = (id) => of(1)
const secondService = (id) => of(2)
const thirdService = (id) => of(3)
const getFirstSecondThird = (id) => {
return firstService(id).pipe(
mergeMap(first =>
secondService(first.secondEntityId).pipe(
mergeMap(second =>
thirdService(second.thirdEntityId).pipe(
map(third => [first, second, third])
)
)
)
)
)
}
getFirstSecondThird(0)
.subscribe(result => console.log('result', result))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.3.3/rxjs.umd.js"></script>
You might use switchMap() instead of mergeMap() if there is the possibility of getFirstSecondThird() being called a second time but before all the fetches of the first call have completed, and you want to discard the first call - for example in an incremental search scenario.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With