Reaktive und proaktive Programmierung mit RxJS

Veröffentlicht

(Ben Copeland) (25. März) , 2020)

Wenn wir ReactiveX-Bibliotheken wie RxJS verwenden, wissen wir, dass wir technisch eine reaktive Programmierlösung implementieren – wir verwenden das RxJS-Konzept beobachtbarer Streams und unser Code reagiert auf neue Ereignisse im Strom. Selbst wenn wir eine reaktive Bibliothek verwenden, ist es möglich, unsere Software eher nach einem proaktiven als nach einem reaktiven Paradigma zu gestalten. Wir werden von diesen Bibliotheken jedoch viel besser bedient, wenn wir eine reaktive Einstellung haben.

Nehmen wir uns einen Moment Zeit, um die Unterscheidung zwischen einem proaktiven und einem reaktiven Ansatz zu identifizieren. Mit proaktiv Ich beziehe mich auf einen Ansatz, bei dem der Herausgeber (oder Betreff) die externen Abonnenten und ihre gewünschten Zustände kennt und Aktualisierungen an die bekannten Abonnenten weiterleitet. Die Kopplung mit dem Verbraucher erfolgt auf der Herausgeberseite. Mit reaktiv beziehe ich mich auf einen Ansatz, bei dem der Abonnent ein Update erhält und es selbst verarbeitet.

Betrachten Sie ein aktuelles Szenario, das wir in IQueue for Clinics erlebt haben. Wir haben eine Angular-App, die stark RxJS verwendet, und wir wollten jede Änderung veröffentlichen, die im ngOnChanges -Lebenszyklus-Hook einer Komponente für einen Stream auftritt, den Abonnenten nutzen können.

Hier ist ein vereinfachtes Beispiel, in dem wir eine Komponente mit drei @Input -Eigenschaften haben und einige Komponenten, die ein Objekt verfolgen möchten, das jeweils den neuesten Wert hat die drei Eigenschaften.

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

Im obigen Beispiel erstellen wir ein Objekt mit jeder der drei möglichen Eigenschaften für das Caching und dann auf ngOnChanges, wir führen Aktualisierungen von einer der drei Eigenschaften in diesem Objekt zusammen und aktualisieren den Stream mit dem neuesten Wert des kombinierten Objekts. Dies ist ein proaktiver Ansatz – das Subjekt kennt die Absicht des Beobachters. Wenn ein anderer Beobachter hinzugefügt wird, der eine andere Absicht hat, oder wenn sich die Absicht des aktuellen Beobachters ändert, muss die Implementierung des Themas geändert werden.

Der obige Ansatz ist im Wesentlichen das, womit wir begonnen haben, dann sind wir gegangen weiter, um es umzugestalten, um es reaktiver zu machen. Dazu mussten wir dafür sorgen, dass das Thema einfach den neuesten Wert ohne Kenntnis der Verbraucher veröffentlicht. Das Folgende ist eine aktualisierte Version der früheren Komponente, die nur aktuelle Updates veröffentlicht.

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

changes$: Subject = new Subject();

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

Und dann behandeln wir die Aggregation zu einem einzelnen Objekt in der Klasse, die verbraucht das Observable.

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

Jetzt veröffentlicht das Observable nur neue Ereignisse, und der Teilnehmer nutzt eine der vielen leistungsstarken Pipes von RxJS, die als Scan bezeichnet werden, um einen akkumulierten Zustand aufrechtzuerhalten der neuesten Werte aller möglichen Eigenschaften, die durch den Stream kommen. Stellen Sie sich Scan als eine Reduzierungsfunktion vor, die bei jedem Ereignis den zuletzt akkumulierten Wert ausgibt. Nach dem Refactoring haben wir also ein allgemeines Thema, das jedes mögliche ngOnChanges -Ereignis auslöst. Wenn mehr @Inputs hinzugefügt werden, werden diese natürlich auch versendet. Alle Abonnenten, die den Stream abhören, können das tun, was sie für den Ereignis-Stream für richtig halten – unabhängig davon, ob sie ein Objekt aller Ereignisse ansammeln oder nur Änderungen an einer einzelnen Eigenschaft abhören.

Während RxJS gibt Mit den Werkzeugen für reaktive Programmierung ist es immer noch möglich, Code zu entwerfen, der einem proaktiveren Paradigma folgt. Um reaktiver zu sein, können wir unsere Veröffentlichung so agnostisch wie möglich halten und Abonnements erlauben, kompliziertere Logik zu implementieren.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.