My component needs to check whether some app preferences are set before API requests are made. Right now I have set it up like this, where I keep my component's data updated on a timer of 2 minutes:
ngOnInit(): void {
this.subscription = timer(0, 120 * 1000).subscribe(() => {
this.shopService.getPreferencesAsObservable().subscribe(preferences => {
if(preferences) {
this.getInitialPendingSlotsOrders();
this.getInitialPendingNoSlotsOrders();
}
});
});
}
getInitialPendingSlotsOrders(){
this.apiService.fetchShopOrders("status=PENDING&only_slots=true").subscribe(orders=> {
/* do stuff with orders */
/* it can happen that I need to get another page of data */
if(orders.next_page){
this.getNextSlotsSetOfPendingOrders();
}
});
}
getInitialPendingNoSlotsOrders(){
this.apiService.fetchShopOrders("status=PENDING").subscribe(orders=> {
/* do stuff with orders */
/* it can happen that I need to get another page of data */
if(orders.next_page){
this.getNextNoSlotsSetOfPendingOrders();
}
});
}
getNextSlotsSetOfPendingOrders() {
this.apiService.fetchNextSetOfOrders(this.nextSlotsUrl).subscribe(nextSetOfOrders => {
/* do stuff with next set of orders */
})
}
getNextNoSlotsSetOfPendingOrders() {
this.apiService.fetchNextSetOfOrders(this.nextNoSlotsUrl).subscribe(nextSetOfOrders => {
/* do stuff with next set of orders */
})
}
I thought that this would work but I have reached a scenario where I see that some extra API calls are being made. I know this has something to do with chaining observables. What can I do to clean this up?
Thank you in advance.
You have multiple nested subscriptions. They lead to multiple open subscriptions which may never be closed. Instead you'd need to use various RxJS operators available to restrict it to a single subscription.
And seeing you need to trigger two requests in parallel, you could also use RxJS forkJoin function.
Refer here for a quick run down.
In short
switchMap operator to map from one observable to anotherfilter operator to continue the operator chain based on a conditionforkJoin to combine and trigger multiple observables in parallelTry the following
ngOnInit(): void {
this.subscription = timer(0, 120 * 1000).pipe(
switchMap(() => this.shopService.getPreferencesAsObservable()),
filter(preferences => !!preferences) // emit only if `preferences` is defined and truthy
switchMap(() =>
forkJoin({
slots: getInitialPendingOrders(true),
noSlots: getInitialPendingOrders(false)
})
)
).subscribe({
next: ({ slots, noSlots }) => {
// do stuff with orders from `slots` and `noSlots` responses
},
error: (error: any) => {
// handle error
}
});
}
getInitialPendingOrders(slots: boolean): Observable<any> {
return this.apiService.fetchShopOrders("status=PENDING" + slots ? "&only_slots=true" : '');
}
As a rule of thumb, you should return the observable and subscribe only where it's response is required. In your case you could pipe in a switchMap to each argument of the forkJoin and return an observable conditionally. When you do not wish to return anything return RxJS constand EMPTY to emit results from the forkJoin. Note that forkJoin would only emit when all it's source observables complete.
ngOnInit(): void {
this.subscription = timer(0, 120 * 1000).pipe(
switchMap(() => this.shopService.getPreferencesAsObservable()),
filter(preferences => !!preferences) // emit only if `preferences` is defined and truthy
switchMap(() =>
forkJoin({
slots: getInitialPendingOrders(true).pipe(
switchMap((orders: any) => {
/* do stuff with orders */
return orders.next_page ? this.getNextSlotsSetOfPendingOrders() : EMPTY;
})
),
noSlots: getInitialPendingOrders(false).pipe(
switchMap((orders: any) => {
/* do stuff with orders */
return orders.next_page ? this.getNextNoSlotsSetOfPendingOrders() : EMPTY;
})
)
})
)
).subscribe({
next: ({ slots, noSlots }) => {
// do stuff with next set of orders from `slots` and `noSlots`
},
error: (error: any) => {
// handle error
}
});
}
getInitialPendingOrders(slots: boolean): Observable<any> {
return this.apiService.fetchShopOrders("status=PENDING" + !!slots ? "&only_slots=true" : '');
}
getNextSlotsSetOfPendingOrders(): Observable<any> {
return this.apiService.fetchNextSetOfOrders(this.nextSlotsUrl);
}
getNextNoSlotsSetOfPendingOrders(): Observable<any> {
return this.apiService.fetchNextSetOfOrders(this.nextNoSlotsUrl);
}
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