angular - Podmíněné rozšiřování pozorovatelného řetězce
Problém:
Mám více podmínek k ověření. Některé z nich mají za následek stejnou pozorovatelnou.
Průběh je následující:
- Kontroluji, zda je formulář uložen lokálně
- Pokud tomu tak je, zobrazím uživateli text 1
- Pokud ne, pak zkontroluji, zda má server tento formulář v databázi
- Pokud to server má, pak ukážu text 2
- Pokud ne, zobrazím text 1
Existuje způsob, jak se vyhnout tomu, aby více podmínek vedlo ke stejnému výsledku a bylo kopírováno a vloženo?
Teď to vypadá takto
iif(
() => this.savedLocally,
this.showText1(),
this.checkOnServer().pipe(
switchMap(onServer => iif(
() => onServer,
this.showText2(),
this.showText1()
))
)
)
Pokud existuje ještě jedna podmínka, pak má ještě jednu úroveň s opakováním this.showText1() někde.
Co hledám, je přepracovat tuto logiku tak, aby byla méně opakující se.
Trik spočívá v tom, že potřebuji provést tři kontroly (následující se provede pouze tehdy, když předchozí má očekávaný výsledek) a všechny tyto jsou asynchronní.
Kdyby to byl jednoduchý if, použil bych if (cond1 && cond2 && cond3), ale tady to tak jednoduché není. Mám přísné pořadí kontrol a každá kontrola závisí na předchozím výsledku.
Odpověď č. 1
Opravdu musíte používat observables? Vypadá to, že byste to mohl napsat čistěji jednoduchou asynchronní funkcí
async () => {
if(this.savedLocally) return this.showText1();
const isOnServer = await this.checkOnServer();
if(isOnserver) return this.showText2();
const isAnotherCondition = await this.checkAnotherCondition();
if(isAnotherCondition ) return this.showText3();
return this.showText1();
}
Odpověď č. 2
Popisujete kolekci pozorovatelných objektů, kde každý pozorovatelný objekt určuje, zda by měl být použit další pozorovatelný objekt.
Pokud hodnota pozorovatelného nesplňuje podmínku, použije se hodnota dalšího pozorovatelného, dokud není dosaženo konce kolekce, ale pokud hodnota pozorovatelného podmínku splňuje, nepotřebujete žádné další pozorovatelné.
Když se observable dokončí, potřebujete akumulaci podmínek.
Chcete něco škálovatelného. Umožňující vám asynchronně řešit dlouhý seznam podmínek, ale zastavit, když není potřeba další logiky.
Můžete použít kombinaci vysoce-řádového observable, reducerů a objektu stavu.
Vyšší řád pozorovatelného je pozorovatelné, které vydává jiné pozorovatelné. Například;
const higherOrder$ = of(of(true));
Pojďme použít stavový objekt k uchování podmínky satisfy a akumulaci hodnot.
interface State {
// continue when value is false
satisfy: boolean;
// accumulation of data
values: {[key:string]: any};
}
Začněme s výchozí hodnotou stavu.
const DEFAULT_STATE = {
satisfy: false,
values: {
savedLocally: false,
onServer: false
}
};
Definujme naše pozorovatelné veličiny, které budou vydávat hodnoty, jež budou akumulovány.
- Každý pozorovatelný objekt bude emitovat
satisfyavalues. - Dokud je
satisfyfalse, bude použit další pozorovatelný prvek. - Každá
hodnotabude akumulována v konečném výsledku.
const savedLocally$ = this.http.get(...).pipe(map(resp => ({
satisfy: Boolean(resp), values: {savedLocally: resp}
})));
const checkOnServer$ = this.http.get(...).pipe(map(resp => ({
satisfy: Boolean(resp), values: {onServer: resp}
})));
Můžeme vytvořit vysoce řádový observable, přepínat na každý observable, shromažďovat výsledky a zastavit se na první uspokojivé hodnotě.
Zde používám operátor scan() k akumulaci hodnot přes každou vyemitovanou hodnotu.
of(savedLocally$, checkOnServer$).pipe(
switchMap(ob$ => ob$),
scan((acc, next) => ({
satisfy: next.satisfy,
values: {...acc.values, ...next.values}
}), DEFAULT_STATE),
filter(state => state.satisfy),
first(),
map(state => state.values)
).subscribe(values => console.log(values));
Teď nevím, jestli je to jednodušší než to, co máte teď, ale dokáže to škálovat a podporovat dlouhou sbírku observables.
V tomto příkladu switchMap() nemá přístup k předchozím hodnotám stavu. Je to možné udělat, ale je to složitější a potřeboval bych čas, abych přemýšlel, jak to udělat.
