Reactief versus proactief programmeren met RxJS

(Ben Copeland) (25 maart) , 2020)

Als we ReactiveX-bibliotheken zoals RxJS gebruiken, weten we dat we technisch een reactieve programmeeroplossing implementeren – we gebruiken het concept van waarneembare streams van RxJS en onze code reageert op nieuwe gebeurtenissen in de stroom. Maar zelfs als we een reactieve bibliotheek gebruiken, is het mogelijk om onze software te ontwerpen volgens een meer proactief dan een reactief paradigma. We zijn echter veel beter gediend met deze bibliotheken als we een reactieve instelling hebben.

Laten we even de tijd nemen om het onderscheid te identificeren tussen een proactieve en reactieve benadering. Met proactief bedoel ik een benadering waarbij de uitgever (of onderwerp) op de hoogte is van de externe abonnees en hun gewenste status, en updates naar de bekende abonnees pusht. De koppeling met de consument gebeurt aan de uitgeverzijde. Met reactief verwijs ik naar een benadering waarbij de abonnee een update ontvangt en deze zelf afhandelt.

Beschouw een recent scenario dat we tegenkwamen op IQueue for Clinics. We hebben een Angular-app die veel gebruik maakt van RxJS en we wilden elke wijziging publiceren die optreedt in de ngOnChanges lifecycle hook van een component naar een stream die abonnees zouden kunnen gebruiken.

Hier is een vereenvoudigd voorbeeld, waarin we een component hebben met drie @Input eigenschappen, en we hebben enkele componenten die een object willen bijhouden met de meest recente waarde van elk van de drie eigenschappen.

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

In het bovenstaande voorbeeld maken we een object met elk van de drie mogelijke eigenschappen voor caching, en vervolgens op ngOnChanges, we voegen updates van elk van de drie eigenschappen samen in dat object, en we werken de stream bij met de laatste waarde van het gecombineerde object. Dit is een proactieve benadering – het onderwerp kent de bedoeling van de waarnemer. Als een andere waarnemer wordt toegevoegd die een andere intentie heeft, of als de intentie van de huidige waarnemer verandert, moet de implementatie van het onderwerp worden gewijzigd.

De bovenstaande benadering is in wezen waar we mee begonnen, daarna gingen we om het te refactoren om het reactiever te maken. Om dat te laten gebeuren, moesten we het onderwerp gewoon de laatste waarde publiceren zonder enige medeweten van de consument. Het volgende is een bijgewerkte versie van de eerdere component die alleen de huidige updates publiceert.

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

changes$: Subject = new Subject();

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

En dan behandelen we de aggregatie in een enkel object in de klasse die verbruikt het waarneembare.

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 publiceert het waarneembare alleen nieuwe gebeurtenissen, en de abonnee gebruikt een van de vele krachtige leidingen van RxJS, scan genaamd, om een ​​geaccumuleerde toestand te behouden van de laatste waarden van alle mogelijke eigenschappen die door de stream komen. Beschouw scan als een verkleiningsfunctie die bij elke gebeurtenis de laatste geaccumuleerde waarde uitvoert. Dus na de refactoring hebben we een onderwerp voor algemene doeleinden dat elke mogelijke ngOnChanges -gebeurtenis verzendt. Als er meer @Inputs worden toegevoegd, worden die natuurlijk ook verzonden. Alle abonnees die naar de stream luisteren, kunnen doen wat ze nodig achten bij de evenementstream – of dat nu een object van alle evenementen is, of alleen luisteren naar wijzigingen op een enkele eigenschap.

Terwijl RxJS geeft Ons de tools om reactief programmeren te doen, is het nog steeds mogelijk om code te ontwerpen die een meer proactief paradigma volgt. Om reactiever te zijn, kunnen we onze publicaties zo agnostisch mogelijk houden en toestaan ​​of abonnementen om meer gecompliceerde logica te implementeren.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *