© Syda Productions/Shutterstock.com
Einblick in die interne Architektur von RxJS und seinen Operatoren

Die Anatomie von RxJS-Operatoren


Operatoren stellen einen Mechanismus zur Verfügung, um komplexe Funktionen in les- und wartbarer Weise zu implementieren. Sie bieten einen immensen Mehrwert bei der Arbeit mit RxJS. Allerdings wissen nur wenige über die interne Implementierung von Operatoren und die dahinterliegenden Gründe Bescheid.

RxJS ist die De-facto-Standardbibliothek für reaktive Programmierung im JavaScript-Ökosystem. Sie ist bekanntermaßen komplex und insbesondere die Etablierung einer reaktiven Denkweise ist eine Herausforderung. Reaktive Programmierung befasst sich im Allgemeinen mit der Propagierung von Veränderungen. RxJS implementiert diesen Mechanismus der Änderungspropagierung durch die Observable-Entität. Operatoren können auf Observables angewendet werden, um Manipulationen durchzuführen und ein Observable mit einem bestimmten Verhalten zu erweitern. Solche Operatoren können auf ein Observable angewendet werden, indem man sie an die pipe-Methode der Observable-Instanz übergibt. Die meisten Entwickler, die RxJS nutzen, haben bereits regelmäßig Operatoren verwendet.

Allein die große Vielfalt von Operatoren erschwert das Erlernen von RxJS deutlich. Noch schwieriger ist es, herauszufinden, welche Operatoren zur Lösung eines bestimmten Problems geeignet sind. Alle Operatoren auf einmal zu erlernen, mag als der schnellste Weg erscheinen, um RxJS zu meistern, jedoch ist ein fundamentales Verständnis für die interne Architektur der Operatoren deutlich zielführender. Zu Beginn sollten wir zunächst klären, was überhaupt als Operator angesehen werden kann.

Streng definiert ist ein Operator nur eine Funktion, die eine bestimmte Schnittstelle implementiert und sich daher an eine bestimmte Struktur halten muss. Das bedeutet jedoch nicht, dass alle von RxJS bereitgestellten Funktionen Operatoren sind. Aus meiner Sicht müssen Funktionen zwei Kriterien erfüllen, damit sie als Operator betrachtet werden können:

  1. Ein Operator muss komponierbar sein.

  2. Ein Operator muss das Verhalten eines Observables erweitern.

Operatorkriterien

Ein Operator muss komponierbar sein

Komposition bedeutet, dass Operatoren auf ein bestehendes Observable angewendet werden müssen und ein neues Observable zurückgeben wird. Obwohl Funktionen wie from, of oder fromEvent ein neues Observable erstellen, werden sie nicht als Operatoren betrachtet, da sie nicht auf bestehende Observables angewendet werden können. Wenn eine Funktion nicht komponierbar ist, ist es nicht möglich, sie innerhalb der pipe-Methode zu nutzen.

Ein weiteres Beispiel für eine RxJS-Funktion, die nicht als Operator angesehen werden kann, ist die multicast-Funktion. Abhängig von der benutzten Funktionssignatur kann sie ein ConnectableObservable zurückgeben, das eine connect-Methode zur Verfügung stellt. Diese connect-Methode kann aufgerufen werden, um das Quell-Observable manuell zu abonnieren (subscribe). Da die multicast-Funktion ein ConnectableObservable zurückgeben kann, ist es nicht möglich, danach andere Operatoren aufzurufen, da Operatoren nur auf eine echte Observable-Instanz angewendet werden können.

Ein Operator muss das Verhalten eines Observables erweitern

Das beste Beispiel für einen Operator, der Verhalten zu einem bestehenden Observable hinzufügt, ist der map-Operator. map nimmt die Werte eines Observable, wendet eine Projektionsfunktion auf jeden einzelnen an und gibt ein neues Observable zurück. Die Verwendung des map-Operators erlaubt die Manipulation aller von einem Observable ausgegebenen Werte.

In Listing 1 ist das für sämtliche Operatoren zugrundeliegenden Interface zu sehen, das aus der RxJS-Codebasis extrahiert wurde. Die Interfacedefinition zeigt, dass ein Operator eine Funktion sein muss, die eine Funktion (auch als Higher-Order Function bekannt) zurückgibt, die ein Observable als Quellparameter empfängt und ein neues Observable zurückgibt. Die Verwendung einer Higher-Order Function stellt sicher, dass jedes Observable standardmäßig „träge“ ist. Das bedeutet, dass der Code der zurückgegebenen Funktion erst ausgeführt wird, wenn das Observable abonniert wird.

Listing 1

interface UnaryFunction<T, R>{(source: T): R;} interface OperatorFunctio...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang