RxJS Operators
Ziele
- Du kennst die verschiedenen RxJS-Operatoren und kannst diese anwenden.
Operators
RxJS bietet eine Vielzahl von Operatoren, die auf Observables angewendet werden können, um sie zu transformieren, zu filtern, zu kombinieren und vieles mehr. Folgend werden die wichtigsten vorgestellt.
Piping
Die pipe()
ist ein wichtiger Operator, der es ermöglicht, eine Observable-Kette zu erstellen, indem verschiedene Operatoren nacheinander angewendet werden. Der Piping-Operator wird verwendet, um die Lesbarkeit und Wartbarkeit von Observable-Ketten zu verbessern, indem man sie in kleinere und leichter zu verstehende Abschnitte unterteilt.
Wie eine map funktioniert, kannst du hier nachlesen.
|
|
Creation Operators
from
Der from
-Operator wandelt eine Reihe von Werten oder ein Iterable in ein Observable um. Er akzeptiert eine Quelle von Werten, wie beispielsweise ein Array oder eine Map, und gibt ein Observable zurück, das die Werte dieser Quelle nacheinander emittiert.
Der from
-Operator ist daher primär für Werte geeignet, die aus verschiedenen dynamischen Quellen resultieren. Er splittet die Eingabe, was der of
-Operator nicht tut. Mögliche Argumente sind dabei subscribable Objects, Promises, Observable-artige Objekte, Arrays, iterierbare Datenstrukturen oder ein Array-artige Objekte.
|
|
of
Der of
-Operator wandelt wie der from
-Operator auch eine feste Anzahl von Werten in ein Observable um. Der Operator akzeptiert jedoch eine beliebige Anzahl von Argumenten und gibt ein Observable zurück, das diese Argumente in der Reihenfolge ihres Auftretens emittiert.
Dieser Operator ist besonders nützlich, um eine feste Anzahl von Werten zu emittieren, die bekannt sind, bevor das Observable abonniert wird. Wenn man eine Quelle von Werten hat, die dynamisch generiert werden, ist es wahrscheinlich sinnvoller, den from
Operator zu verwenden.
|
|
Join Creation Operators
Info: In RxJS bezieht sich “Emission” auf den Wert, der von einem Observable emittiert / ausgegeben wird.
forkJoin
forkJoin
ist ein Kombinationsoperator, der ein Array von Observables akzeptiert und wartet, bis alle Observables ihre Emissionen abgeschlossen haben, bevor er ein neues Observable zurückgibt. Das zurückgegebene Observable gibt ein Array von Werten zurück, das den letzten Wert jedes Observables enthält, das dem forkJoin
Operator übergeben wurde.
Dieser Operator ist besonders nützlich, wenn man mehrere Quellen von Daten hat, die parallel verarbeitet werden können, und man auf alle Ergebnisse warten muss, bevor man mit der nächsten Phase der Verarbeitung fortfahren kann.
Zu beachten ist, dass der Operator fehlschlägt, wenn eines der übergebenen Observables einen Fehler emittiert, bevor es seine Emissionen abgeschlossen hat.
|
|
concat
Der concat
-Operator kombiniert mehrere Observables sequentiell, indem er die Emissionen des ersten Observables vollständig verarbeitet, bevor er mit dem nächsten Observable fortfährt. Das bedeutet, dass das zweite Observable erst dann subscribed wird, wenn das erste Observable seine Emissionen vollständig abgeschlossen hat und so weiter.
Dieser Operator ist besonders nützlich, wenn man sicherstellen muss, dass bestimmte Aktionen in einer bestimmten Reihenfolge ausgeführt werden, oder wenn man die Emissionen von Observables in einer bestimmten Sequenz verarbeiten muss.
Zu beachten ist jedoch, dass der Operator blockierend ist und erst dann zur nächsten Phase der Verarbeitung übergeht, wenn das vorherige Observable abgeschlossen wurde.
|
|
merge
Der merge
-Operator kombiniert auch mehrere Observables, indem er die Emissionen aller Observables in einem einzigen Observable zusammenführt. Im Gegensatz zum concat
-Operator führt der merge
-Operator die Emissionen parallel aus, unabhängig davon, welches Observable die Emissionen zuerst ausgibt.
Beachten muss man jedoch, dass dieser Operator keine Garantie für die Reihenfolge der Emissionen gibt und dass es möglich ist, dass die Emissionen der Observables sich gegenseitig überschneiden.
|
|
zip
Der zip
Operator kombiniert die Emissionen mehrerer Observables zu einer einzelnen Emission. Dabei werden die Emissionen jedes Observables zu einer Gruppe kombiniert, und sobald alle Observables eine Emission abgegeben haben, wird diese Gruppe als Emission des resultierenden Observables ausgegeben.
Dieser Operator ist besonders nützlich, wenn man mehrere Datenströme kombiniert und sicherstellen muss, dass alle Daten synchronisiert sind. Man muss jedoch beachten, dass der Operator die Emissionen jedes Observables zu einer Gruppe kombiniert, was bedeutet, dass die Grösse jeder Emission vom Observable mit der geringsten Anzahl an Emissionen begrenzt wird.
Auch zu beachten ist, dass der Operator darauf wartet, dass alle Observables eine Emission abgeben, bevor er eine Emission ausgibt. Wenn ein Observable keine Emission abgibt, wird das resultierende Observable keine Emissionen ausgeben.
Der Hauptunterschied vom zip
-Operator zum forkJoin
-Operator ist, dass mit zip
die reinkommenden Werte Schritt für Schritt kombiniert werden, während forkJoin
die Werte erst emittiert, nachdem alle Input-Observables completed wurden.
|
|
Transformation Operators
map
Der meist verwendete RxJS-Operator ist der map
Operator. Dieser wird verwendet, um die Werte eines Observables zu transformieren. Es gibt viele Anwendungsfälle für diesen Operator, einschliesslich der Umwandlung von Daten in ein anderes Format, der Extraktion von bestimmten Werten aus einem Datenstrom oder der Anwendung einer Funktion auf jeden Wert in einem Observable.
|
|
mergeAll/mergeMap
Es kann passieren, dass ein Observable eine Methode aufruft, die ein weiteres Observable zurückgibt. Dieser Verhalten kommt oft bei API-Abfragen vor. Somit hat man dann ein äusseres und inneres Observable. Um nun an die Daten zu gelangen, müsste man auf beide subscriben, was auch möglich ist:
|
|
Das ist aber alles andere als optimal, weshalb man alternativ mergeMap
benutzen kann. mergeMap
ist eine Kombination von mergeAll
und map
. mergeAll
erledigt die subscription auf das innere Observable, auf diese Weise muss man dann nicht mehr zweimal subscriben. Zudem merged es das innere Observable in das äussere.
Beispiel mit mergeAll
:
|
|
Beispiel mit mergeMap
:
|
|
Hinweis: Oft wird flatMap
als Alias für mergeMap
verwendet. Lass dich also nicht verwirren, wenn die Begriffe teilweise wechseln.
switchAll/switchMap
switchMap
funktioniert ähnlich wie mergeMap
. Der Operator subscribed auch auf das innere Observable. Auch hier ist switchMap
eine Kombination, und zwar von switchAll
und map
. switchAll
canceled die vorherige Subscription und subscribed auf die neue, wenn ein neues Observable reinkommt.
Hier kannst du nachlesen, wie delay
funktioniert.
Beispiel mit switchAll
:
|
|
Beispiel mit switchMap
:
|
|
concatMap
Auch concatMap
subscribed auf das innere Observable. Der Unterschied zu switchMap
ist, dass concatMap
nicht die Subscription cancelled, wenn ein neues Observable reinkommt. Es subscribed stattdessen solange nicht auf das nächste Observable, bis das momentane fertig ist.
|
|
groupBy
Die groupBy()
-Methode ermöglicht es, ein Observable in mehrere Observables aufzuteilen, die auf der Grundlage eines bestimmten Schlüssels gruppiert sind.
Der Prozess besteht darin, die ursprünglichen Emissionen in Gruppen aufzuteilen, die jeweils auf einen eindeutigen Schlüssel abgebildet werden. Jede Gruppe ist ein eigenes Observable, das alle Emissionen enthält, die diesem Schlüssel zugeordnet sind.
|
|
Filtering Operators
elementAt
elementAt()
wird benutzt, um ein Element eines Observable an der angegebenen Indexposition auszugeben und das Observable zu schliessen. Der Operator gibt ein Observable zurück, das nur das angeforderte Element emittiert und dann vollständig geschlossen wird.
Man kann nach der Indexposition auch einen Defaultwert angeben, welcher zurückgegeben wird, wenn das Element an der angegebenen Indexposition nicht gefunden wurde.
|
|
filter
filter
wird verwendet, um Observable-Elemente zu filtern, die nicht den Bedingungen entsprechen, die in der übergebenen Funktion definiert wurden. Die Filterfunktion gibt ein neues Observable zurück, das nur Elemente enthält, die die Bedingungen der Filterfunktion erfüllen.
|
|
first
Der first
Operator gibt nur das erste Element aus einem Observable zurück. Man kann den Operator aber auch mittels einer Funktion ergänzen, um eine Bedienung zu schaffen, somit wird dann das erste Element zurückgegeben, welches auf diese Bedingung zutrifft. Nach der Funktion kann man auch einen Defaultwert hinzufügen, welcher zurückgegeben werden soll, wenn kein Element gefunden wird. Gibt man keinen Defaultwert an und die Bedingung trifft nicht zu, so wird ein fehler ausgegeben.
|
|
last
Der last
Operator funktioniert genau gleich wie der first Operator. Nur ist es so, dass hier immer das letzte Element zurückgegeben wird.
|
|
skip
Der skip
Operator gibt an, wie viele Ereignisse in einem Observable übersprungen werden sollen, bevor sie an einen Subscriber weitergegeben werden. Es wird verwendet, um den Startpunkt eines Observables zu verschieben.
|
|
take
Der take()
Operator gibt eine bestimmte Anzahl von Werten eines Observables aus und schliesst es dann ab. Der Operator akzeptiert als Parameter die Anzahl der Werte, die ausgegeben werden sollen. Wenn der Parameter nicht angegeben wird, wird standardmässig nur ein Wert ausgegeben. Sind weniger Werte verfügbar als angegeben, gibt der Operator die Anzahl Werte aus, die er zur Verfügung hat.
|
|
takeUntil
takeUntil
ist ein Operator, der ein Observable subscribed, bis ein anderes Observable ein Ereignis ausgibt. Es wird verwendet, um das Subscriben eines Observables basierend auf einem anderen Observable zu beenden.
Das zweite Observable wird als “Abbruch- oder Trigger-Observable” bezeichnet. Sobald das Trigger-Observable ein Ereignis ausgibt, wird das Subscriben des ersten Observables beendet und das Observable gibt keine weiteren Werte mehr aus.
|
|
In der Praxis wird takeUntil
häufig in Kombination mit ngOnDestroy
verwendet. ngOnDestroy()
ist eine Lifecycle-Methode welche aufgerufen wird, bevor eine Komponente aus dem DOM entfernt wird. Somit kann man takeUntil
verwenden, um Subscriptions zu beenden und somit Ressourcen freizugeben und Speicherlecks zu vermeiden.
|
|
Error Handling Operators
catchError
catchError
ist ein Operator, der verwendet wird, um Fehler in Observables zu verarbeiten. Er erlaubt es, ein alternatives Observable zurückzugeben oder eine andere Aktion auszuführen, wenn ein Fehler im ursprünglichen Observable auftritt.
Denn normalerweise ist es so, dass im Falle eines Fehler in einem Observable, das gesamte Observable abgebrochen wird und keine weiteren Werte emittiet werden. Wenn man jedoch catchError
verwendet, kann man eine Funktion angeben, die den Fehler verarbeitet und ein alternatives Observable zurückgibt. Dadurch kann man das Observable weiterlaufen lassen, anstatt es abzubrechen.
|
|
retry
retry()
versucht, das Observable beim ersten Fehler neu zu starten. Dabei wird das gesamte Observable neu subscribed. Wenn das erneute Subscriben wieder einen Fehler erzeugt, wird das Observable erneut neu gestartet und so weiter, bis das erneute Subscriben erfolgreich ist.
Wenn man dies aber nicht unbegrenzt lange machen möchte, bis man erfolgreich ist, kann man die Anzahl der Wiederholungen als Parameter im retry()
-Operator mitgeben.
In der Praxis verwendet man retry()
oft, um API-Requests zu wiederholen, wenn sie fehlschlagen sollten, da sie eventuell zu lange gebraucht haben.
|
|
Utility Operators
tap
Das tap
Operator ermöglicht es, “side actions” auf den Werten auszuführen, die von einem Observable emittiert werden, ohne die emittierten Werte zu verändern. Man kann es verwenden, um die emittierten Werte zu debuggen und zu untersuchen oder um eine Aktion auf Grundlage dieser Werte auszulösen, ohne die Observable-Pipeline zu beeinflussen.
|
|
delay
delay
verzögert das Weiterleiten von Werten eines Observables um eine bestimmte Zeitspanne. Dadurch kann man eine Verzögerung in der Ausführung von Aktionen erzielen, was besonders nützlich ist, wenn man z.B. Animationen oder Zeitabhängigkeiten implementiert.
|
|
Conditional and Boolean Operators
every
Der every
Operator prüft, ob alle Werte, die von einem Observable emittiert werden, eine bestimmte Bedingung erfüllen. Wenn die Bedingung für alle Werte true
zurückgibt, gibt der Operator true
zurück. Wenn die Bedingung für mindestens einen Wert false
zurückgibt, gibt auch der Operator false
zurück.
|
|
find
find
ist ein Operator, welcher es ermöglicht, in einem Observable nach dem ersten Element zu suchen, das eine angegebene Bedingung erfüllt. Wenn das Element gefunden wird, wird es in einem Observable zurückgegeben und die Suche wird beendet.
|
|
findIndex
Der findIndex
Operator gibt den Index des ersten Elements in eines Observables zurück, das die angegebene Bedingung erfüllt. Sie funktioniert ähnlich wie find, aber gibt den Index des Elements statt des Elements selbst zurück.
|
|
isEmpty
isEmpty
ist ein Operator, welcher prüft, ob ein Observable leer ist. Wenn das Observable leer ist, gibt der Operator true
zurück, andernfalls false
.
|
|