angular - Villkorlig expanderande observerbar kedja


Problemet:

Jag har flera villkor att kontrollera. Vissa av dem resulterar i samma observerbara.

Flödet är följande:

  1. Jag kontrollerar om ett formulär sparas lokalt
  2. Om det är så visar jag användaren text 1
  3. Om det inte är det kontrollerar jag om en server har detta formulär i databasen
  4. Om servern har det visar jag text 2
  5. Om det inte gör det visar jag text 1

Finns det ett sätt att undvika flera villkor som leder till samma resultat men som kopieras och klistras in?

Nu ser det ut så här

iif(
  () => this.savedLocally,
  this.showText1(),
  this.checkOnServer().pipe(
    switchMap(onServer => iif(
      () => onServer,
      this.showText2(),
      this.showText1()
    ))
  )
)

Om det finns ett villkor till har det en nivå till med upprepning av this.showText1() någonstans.

Det jag letar efter är att omdesigna denna logik för att vara mindre repetitiv.

Tricket är att jag behöver utföra tre kontroller (nästa utförs endast när den föregående har förväntat resultat) och alla dessa är asynkrona.

Om det var en enkel if skulle jag använda if (cond1 && cond2 && cond3), men här är det inte så enkelt. Jag har en strikt ordning för kontroller och varje kontroll är beroende av föregående resultat.

Svar #1

Behöver du verkligen använda observables? Det verkar som att du skulle kunna skriva detta mer tydligt med en enkel asynkron funktion

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();
}

Svar #2

Du beskriver en samling av observerbara objekt där varje observerbart objekt avgör om det nästa observerbara objektet ska användas.

Om värdet på ett observerbart inte uppfyller ett villkor, används värdet på nästa observerbara tills slutet av samlingen nås, men om ett observerbart uppfyller ett villkor behöver du inga av de återstående observerbara.

När observabeln slutförs behöver du ackumuleringen av villkor.

Du vill ha något skalbart. Som låter dig lösa en lång lista med villkor asynkront, men som stoppar när det inte finns behov av ytterligare logik.

Du kan använda en kombination av ett högordningsobservabel, reducerare och ett tillståndsobjekt.

En högre ordningens observable är en observable som emitterar en observable. Till exempel;

const higherOrder$ = of(of(true));

Låt oss använda ett state-objekt för att hålla satisfy-villkoret och ackumuleringen av värden.

interface State {
   // continue when value is false
   satisfy: boolean;
   // accumulation of data
   values: {[key:string]: any};
}

Låt oss börja med ett standard-värde för tillståndet.

const DEFAULT_STATE = {
   satisfy: false,
   values: {
      savedLocally: false,
      onServer: false
   }
};

Låt oss definiera våra observerbara som kommer att sända värden som kommer att ackumuleras.

  • Varje observerbar kommer att avge satisfy och values.
  • Så länge satisfy är false kommer nästa observerbara att användas.
  • Varje values kommer att ackumuleras i det slutliga resultatet.
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}
})));

Vi kan skapa ett högre ordningens observerbart, byta till varje observerbart, ackumulera resultaten och stoppa vid det första uppfyllda värdet.

Här använder jag scan()-operatorn för att ackumulera värdena över varje emitterat värde.

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));

Nu vet jag inte om detta är enklare än det du har nu, men det kan skalas för att stödja en lång samling av observables.

I det här exemplet har switchMap() inte tillgång till de tidigare tillståndsvärdena. Det går att göra, men det är mer komplicerat och jag skulle behöva tid för att tänka ut hur man gör det.