Reaktiv vs proaktiv programmering med RxJS

(Ben Copeland) (25. mar. , 2020)

Hvis vi bruger ReactiveX-biblioteker såsom RxJS, ved vi, at vi teknisk set implementerer en reaktiv programmeringsløsning – vi bruger RxJSs koncept om observerbare streams, og vores kode reagerer på nye begivenheder i strømmen. Men selvom vi bruger et reaktivt bibliotek, er det muligt at designe vores software efter mere et proaktivt paradigme snarere end et reaktivt. Imidlertid betjenes vi meget bedre af disse biblioteker, hvis vi har en reaktiv sindstilstand.

Lad os tage et øjeblik til at identificere sondringen mellem en proaktiv og reaktiv tilgang. Ved proaktiv henviser jeg til en tilgang, hvor udgiveren (eller emnet) er opmærksom på de eksterne abonnenter og deres ønskede tilstande og skubber opdateringer til de kendte abonnenter. Koblingen med forbrugeren sker på udgiverens side. Ved reaktiv Jeg henviser til en tilgang, hvor abonnenten modtager en opdatering og håndterer den alene.

Overvej et nylig scenario, vi stødte på IQueue for Clinics. Vi har en Angular-app, der i høj grad bruger RxJS, og vi ønskede at offentliggøre hver ændring, der kommer op i en komponents ngOnChanges livscykluskrog til en strøm, som abonnenter kan forbruge.

Her er et forenklet eksempel, hvor vi har en komponent med tre @Input egenskaber, og vi har nogle komponenter, der ønsker at holde styr på et objekt, der har den seneste værdi af hver af de tre egenskaber.

interface IChanges { a: string; b: string; c: string; }

@Component({...})
class SomeComponent {
@Input() a;
@Input() b;
@Input() c;

changes$: BehaviorSubject = new BehaviorSubject({
a: null,
b: null,
c: null,
});

allLatestChanges: IChanges;

ngOnChanges(changes: SimpleChanges) {
if (changes.a) {
changes$.next(Object.assign(allLatestChanges, { a: changes.a.currentValue });
}
if (changes.b) {
changes$.next(Object.assign(allLatestChanges, { b: changes.b.currentValue });
}
if (changes.c) {
changes$.next(Object.assign(allLatestChanges, { c: changes.c.currentValue });
}
}
}

I eksemplet ovenfor opretter vi et objekt med hver af de tre mulige egenskaber til cache, derefter på ngOnChanges, vi fletter opdateringer fra en hvilken som helst af de tre egenskaber til det objekt, og vi opdaterer strømmen med den nyeste værdi af det kombinerede objekt. Dette er en proaktiv tilgang – motivet kender observatørens hensigt. Hvis der tilføjes en anden observatør, der har en anden hensigt, eller hvis hensigten med den nuværende observatør ændres, skal implementeringen af ​​emnet ændres.

Fremgangsmåden ovenfor er i det væsentlige det, vi startede med, så gik vi for at refakte det for at gøre det mere reaktivt. For at dette kunne ske, havde vi brug for, at emnet blot offentliggjorde den nyeste værdi uden nogen viden fra forbrugerne. Følgende er en opdateret version af den tidligere komponent, som kun offentliggør aktuelle opdateringer.

@Component({...})
class SomeComponent {
@Input() a;
@Input() b;
@Input() c;

changes$: Subject = new Subject();

ngOnChanges(changes: SimpleChanges) {
changes$.next(changes);
}
}

Og så håndterer vi sammenlægningen til et enkelt objekt i klassen, der bruger det observerbare.

defaultAcc = {
a: null,
b: null,
c: null,
}

allChanges$ = changes$
.pipe(
scan((acc, curr) =>
Object.assign(acc, Object.fromEntries(
Object.entries(curr).map((k,v) => { [k]: v.currentValue })
)
)), defaultAcc
);

Nu offentliggør det observerbare kun nye begivenheder, og abonnenten udnytter en af ​​RxJSs mange kraftige rør kaldet scanning for at holde en akkumuleret tilstand af de nyeste værdier for alle mulige egenskaber, der kommer gennem strømmen. Tænk på scanning som en reduceringsfunktion, der udsender den seneste akkumulerede værdi på hver begivenhed. Så efter refactoring har vi et generelt emne, der sender alle mulige ngOnChanges begivenheder. Hvis der tilføjes flere @Inputs, vil de naturligvis også sende dem. Alle abonnenter, der lytter til strømmen, kan gøre, hvad de finder passende med begivenhedsstrømmen – hvad enten det er at samle et objekt af alle begivenhederne eller kun lytte til ændringer på en enkelt ejendom.

Mens RxJS giver os værktøjerne til at udføre reaktiv programmering, er det stadig muligt at designe kode, der følger et mere proaktivt paradigme. For at være mere reaktive kan vi holde vores udgivelse så agnostisk som muligt og tillade eller abonnementer at implementere mere kompliceret logik.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *