Letzte Woche beantwortete ich eine RxJS- Frage, in der ich mit einem anderen Community-Mitglied über Folgendes diskutierte: "Soll ich für jede bestimmte Nebenwirkung ein Abonnement erstellen oder sollte ich versuchen, Abonnements im Allgemeinen zu minimieren?" Ich möchte wissen, welche Methologie im Hinblick auf einen vollständig reaktiven Anwendungsansatz zu verwenden ist oder wann von einer zur anderen gewechselt werden muss. Dies wird mir und vielleicht anderen helfen, unnötige Diskussionen zu vermeiden.
Setup-Informationen
- Alle Beispiele sind in TypeScript
- Zur besseren Fokussierung auf die Frage keine Verwendung von Lebenszyklen / Konstruktoren für Abonnements und um im Framework unabhängig zu bleiben
- Stellen Sie sich vor: Abonnements werden in Konstruktor / Lebenszyklus-Init hinzugefügt
- Stellen Sie sich vor: Das Abbestellen erfolgt im Lebenszyklus zerstören
Was ist eine Nebenwirkung (Winkelprobe)
- Update / Eingabe in der Benutzeroberfläche (zB
value$ | async
) - Output / stromaufwärts einer Komponente (z
@Output event = event$
) - Interaktion zwischen verschiedenen Diensten in verschiedenen Hierarchien
Beispielhafter Anwendungsfall:
- Zwei Funktionen:
foo: () => void; bar: (arg: any) => void
- Zwei beobachtbare Quellen:
http$: Observable<any>; click$: Observable<void>
foo
wird aufgerufen, nachdemhttp$
ausgegeben wurde und keinen Wert benötigtbar
wird nachclick$
emits aufgerufen , benötigt aber den aktuellen Wert vonhttp$
Fall: Erstellen Sie ein Abonnement für jede bestimmte Nebenwirkung
const foo$ = http$.pipe(
mapTo(void 0)
);
const bar$ = http$.pipe(
switchMap(httpValue => click$.pipe(
mapTo(httpValue)
)
);
foo$.subscribe(foo);
bar$.subscribe(bar);
Fall: Abonnements im Allgemeinen minimieren
http$.pipe(
tap(() => foo()),
switchMap(httpValue => click$.pipe(
mapTo(httpValue )
)
).subscribe(bar);
Meine eigene Meinung kurz
Ich kann die Tatsache verstehen, dass Abonnements Rx-Landschaften zunächst komplexer machen, weil Sie sich überlegen müssen, wie Abonnenten die Pipe beeinflussen sollen oder nicht (teilen Sie Ihre beobachtbaren oder nicht). Aber je mehr Sie Ihren Code trennen (je mehr Sie sich konzentrieren: Was passiert wann), desto einfacher ist es, Ihren Code in Zukunft zu warten (zu testen, zu debuggen, zu aktualisieren). In diesem Sinne erstelle ich immer eine einzige beobachtbare Quelle und ein einziges Abonnement für alle Nebenwirkungen in meinem Code. Wenn zwei oder mehr Nebenwirkungen, die ich habe, durch genau dieselbe beobachtbare Quelle ausgelöst werden, teile ich meine beobachtbaren und teile jede Nebenwirkung einzeln, da sie unterschiedliche Lebenszyklen haben kann.
quelle
takeUntil
in Kombination mit einer Funktion, von der aus aufgerufen wirdngOnDestroy
. Es ist ein Einzeiler, der dies dem Rohr hinzufügt :takeUntil(componentDestroyed(this))
. stackoverflow.com/a/60223749/5367916Wenn die Optimierung von Abonnements Ihr Endspiel ist, gehen Sie doch zum logischen Extrem und folgen Sie einfach diesem allgemeinen Muster:
Das ausschließliche Ausführen von Nebenwirkungen beim Tippen und Aktivieren mit Zusammenführen bedeutet, dass Sie immer nur ein Abonnement haben.
Ein Grund, dies nicht zu tun, ist, dass Sie viel von dem kastrieren, was RxJS nützlich macht. Welches ist die Fähigkeit, beobachtbare Streams zu erstellen und Streams nach Bedarf zu abonnieren / abbestellen.
Ich würde argumentieren, dass Ihre Observablen logisch zusammengesetzt und nicht im Namen der Reduzierung von Abonnements verschmutzt oder verwirrt sein sollten. Sollte der foo-Effekt logisch mit dem Balkeneffekt gekoppelt werden? Benötigt einer den anderen? Werde ich jemals möglicherweise nicht wollen, dass Foo ausgelöst wird, wenn http $ ausgegeben wird? Schaffe ich eine unnötige Kopplung zwischen nicht verwandten Funktionen? Dies sind alles Gründe, um zu vermeiden, dass sie in einem Stream zusammengefasst werden.
Dies alles berücksichtigt nicht einmal die Fehlerbehandlung, die mit IMO mit mehreren Abonnements einfacher zu verwalten ist
quelle