typescript - Ako spájať Angular observables?
Moja komponenta potrebuje skontrolovať, či sú niektoré nastavenia aplikácie nastavené pred tým, než sa vykonajú požiadavky API. Momentálne som to nastavil takto, kde si dáta mojej komponenty udržiavam aktualizované pomocou časovača na 2 minúty:
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 */
})
}
Myslel som si, že toto bude fungovať, ale dostal som sa do situácie, kde vidím, že sa vykonáva niekoľko ďalších volaní API. Viem, že to súvisí s reťazením observable. Čo môžem urobiť, aby som to vyčistil?
Vopred ďakujem.
Odpoveď č. 1
Máte viacero vnorených odberov. Viedlo by to k viacerým otvoreným odberom, ktoré nemusia byť nikdy uzavreté. Namiesto toho by ste mali použiť rôzne dostupné operátory RxJS na obmedzenie tohto procesu na jediný odber.
A keďže potrebujete spustiť dve požiadavky paralelne, môžete tiež použiť funkciu RxJS forkJoin.
Odkážte sa sem pre rýchly prehľad.
Stručne
- operator
switchMapna mapovanie z jedného observable na iný - Operátor
filterna pokračovanie reťazca operátorov na základe podmienky forkJoinna kombinovanie a spustenie viacerých observables paralelne
Vyskúšajte nasledovné
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" : '');
}
Aktualizovať
Ako základné pravidlo by ste mali vracať observable a subscribe robiť iba tam, kde je potrebná jeho odpoveď. Vo vašom prípade by ste mohli použiť switchMap na každý argument forkJoin a podmienečne vrátiť observable. Keď nechcete nič vrátiť, vráťte konštantu RxJS EMPTY, aby sa emitovali výsledky z forkJoin. Všimnite si, že forkJoin bude emitovať iba vtedy, keď všetky jeho zdrojové observables dokončia.
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);
}
Odpoveď č. 2
Ako základné pravidlo nikdy nezvádzajte subscribe vo vnútri subscribe (samozrejme, existujú výnimky).
Odporúčal by som vám prečítať si Is it good way to call subscribe inside subscribe? a pozrieť sa na rôzne operátory (forkJoin, mergeMap, ...) ktoré RxJs ponúka na kombinovanie observables sekvenčne, paralelne... v závislosti od vašich požiadaviek.
