Heim >Web-Frontend >js-Tutorial >Einführung in die funktionale reaktive Programmierung mit RXJs
Dieser Artikel wurde von Moritz Kröger, Bruno Mota und Vildan Softic überprüft. Vielen Dank an alle Peer -Rezensenten von SitePoint, die SitePoint -Inhalte so gut wie möglich gemacht haben!
Bevor wir uns mit dem Thema befassen, müssen wir die entscheidende Frage beantworten: Was ist reaktive Programmierung? Bis heute ist die beliebteste Antwort, dass die reaktive Programmierung die Programmierung mit gleichzeitigen Datenströmen ist. Meistens werden wir das durch Asynchron ersetzte Wort feststellen, aber wir werden später sehen, dass der Stream nicht asynchron sein muss.Es ist leicht zu erkennen, dass der Ansatz „Alles ist ein Stream“ direkt auf unsere Programmierprobleme angewendet werden kann. Schließlich ist eine CPU nichts weiter als ein Gerät, das einen Informationsstrom verarbeitet, der aus Anweisungen und Daten besteht. Unser Ziel ist es, diesen Strom zu beobachten und zu transformieren, wenn bestimmte Daten.
Die Prinzipien der reaktiven Programmierung sind für JavaScript nicht vollständig neu. Wir haben bereits Dinge wie Eigenschaftenbindung, das EventEmitter -Muster oder Node.js -Streams. Manchmal ist die Eleganz dieser Methoden mit einer verminderten Leistung, übermäßig komplizierten Abstraktionen oder Problemen beim Debuggen verbunden. Normalerweise sind diese Nachteile im Vergleich zu den Vorteilen der neuen Abstraktionsschicht minimal. Unsere minimalen Beispiele werden natürlich nicht die übliche Anwendung widerspiegeln, sondern so kurz und präzise wie möglich.
ohne weiteres, lassen Sie uns die Hände schmutzig machen, indem wir mit den reaktiven Erweiterungen für die JavaScript (RXJS) -Bibliothek spielen. RXJS verwendet viel Ketten, eine beliebte Technik, die auch in anderen Bibliotheken wie JQuery verwendet wird. Eine Anleitung zur Verkettung von Methoden (im Kontext von Ruby) ist auf SitePoint verfügbar.
Bevor wir in RXJS eintauchen, sollten wir einige Beispiele auflisten, mit denen wir später arbeiten können. Dies wird auch die Einführung in reaktive Programmierung und Streams im Allgemeinen abschließen.
Im Allgemeinen können wir zwei Arten von Strömen unterscheiden: intern und extern. Während ersterer als künstlich und innerhalb unserer Kontrolle angesehen werden kann, stammen letztere aus Quellen, die über unsere Kontrolle hinausgehen. Externe Streams können (direkt oder indirekt) aus unserem Code ausgelöst werden.
Normalerweise warten Streams nicht auf uns. Sie passieren, ob wir mit ihnen umgehen können oder nicht. Wenn wir beispielsweise Autos auf einer Straße beobachten möchten, können wir den Autos nicht neu starten. Der Strom geschieht unabhängig von, wenn wir ihn beobachten oder nicht. In der RX -Terminologie nennen wir dies eine heiße Beobachtbare . RX führt auch kalte Observables ein, die sich eher wie Standard -Iteratoren verhalten, so dass die Informationen aus dem Stream aus allen Elementen für jeden Beobachter bestehen.
Die folgenden Bilder veranschaulichen einige externe Arten von Streams. Wir sehen, dass (früher gestartete) Anfragen und im Allgemeinen im Allgemeinen auf Webhaken eingerichtet werden, sowie UI -Ereignisse wie Maus- oder Tastaturinteraktionen. Schließlich können wir auch Daten von Geräten erhalten, z. B. GPS -Sensoren, einem Beschleunigungsmesser oder anderen Sensoren.
Das Bild enthielt auch einen Stream, der als Nachrichten bezeichnet wurde. Nachrichten können in mehreren Formen angezeigt werden. Eine der einfachsten Formen ist eine Kommunikation zwischen unserer Website und einer anderen Website. Weitere Beispiele sind die Kommunikation mit Websockets oder Web Workers. Lassen Sie uns einen Beispielcode für letztere sehen.
Der Code des Arbeiters wird unten dargestellt. Der Code versucht, die Primzahlen von 2 bis 10 10 zu finden. Sobald eine Zahl gefunden wurde, wird das Ergebnis gemeldet.
<span>(function (start<span>, end</span>) { </span> <span>var n = start - 1; </span> <span>while (n++ < end) { </span> <span>var k = Math.sqrt(n); </span> <span>var found = false; </span> <span>for (var i = 2; !found && i <= k; ++i) { </span> found <span>= n % i === 0; </span> <span>} </span> <span>if (!found) { </span> <span>postMessage(n.toString()); </span> <span>} </span> <span>} </span><span>})(2, 1e10); </span>
klassischisch ist der Webarbeiter (angenommen, dass er in der Datei Prime.js liegt) wie folgt. Für die Kürze überspringen wir Schecks über die Unterstützung und die Legalität des zurückgegebenen Ergebniss der Webarbeiter.
<span>var worker = new Worker('prime.js'); </span>worker<span>.addEventListener('message', function (ev) { </span> <span>var primeNumber = ev.data * 1; </span> <span>console.log(primeNumber); </span><span>}, false); </span>
Weitere Details zu Web Workers und Multi-Threading mit JavaScript finden Sie im Artikel Parallel JavaScript mit parallel.js.
In Anbetracht des obigen Beispiels wissen wir, dass Primzahlen einer asymptotischen Verteilung zwischen den positiven Ganzzahlen folgen. Für x bis ∞ erhalten wir eine Verteilung von x / log (x). Dies bedeutet, dass wir am Anfang mehr Zahlen sehen werden. Hier sind die Schecks auch viel billiger (d. H. Wir erhalten zu Beginn viel mehr Primzahlen pro Zeiteinheit als später.)Dies kann mit einer einfachen Zeitachse und Blobs für die Ergebnisse veranschaulicht werden:
Ein nicht bezogenes, aber ähnliches Beispiel kann anhand der Eingabe eines Benutzers in ein Suchfeld angegeben werden. Zunächst kann der Benutzer begeistert sein, etwas einzugeben, nach dem sie suchen können. Je spezifischer seine Anfrage jedoch wird, desto größer wird der Zeitunterschied zwischen den Schlüsselstrichen. Die Möglichkeit zu geben, Live -Ergebnisse zu zeigen, ist definitiv wünschenswert, um dem Benutzer bei der Verengung seiner Anfrage zu helfen. Was wir jedoch nicht wollen, ist eine Anfrage für jeden wichtigen Schlaganfall, zumal die ersten sehr schnell und ohne nachzudenken oder die Notwendigkeit zu spezialisieren.
In beiden Szenarien besteht die Antwort darin, frühere Ereignisse über ein bestimmtes Zeitintervall zu aggregieren. Ein Unterschied zwischen den beiden beschriebenen Szenarien besteht darin, dass die Primzahlen immer nach dem angegebenen Zeitintervall angezeigt werden sollten (d. H. Einige der Primzahlen sind in der Präsentation nur möglicherweise verzögert). Im Gegensatz dazu würde die Suchabfrage nur dann eine neue Anfrage auslösen, wenn während des angegebenen Intervalls kein Schlüsselschlag aufgetreten ist. Daher wird der Timer zurückgesetzt, sobald ein Schlüsselschlag erkannt wurde.
rx ist eine Bibliothek zum Komponieren asynchroner und ereignisbasierter Programme mit beobachtbaren Sammlungen. Es ist bekannt für seine deklarative Syntax und Kompositionsfähigkeit, während ein einfaches Zeit- und Fehlermodell für die Handhabung und Fehler eingeführt wird. Wenn wir an unsere früheren Beispiele denken, interessieren wir uns besonders für die Zeithandhabung. Trotzdem werden wir sehen, dass es in RXJs viel mehr gibt, von denen wir profitieren können.
Die grundlegenden Bausteine von RXJs sind Observables (Produzenten) und Beobachter (Verbraucher). Wir haben bereits die beiden Arten von Observablen erwähnt:
kalte Observable beziehen sich normalerweise auf Arrays oder Einzelwerte, die in RXJs verwendet werden sollen. Zum Beispiel erstellt der folgende Code ein kaltes Beobachtbar, das vor Abschluss nur einen einzelnen Wert ergibt:
<span>(function (start<span>, end</span>) { </span> <span>var n = start - 1; </span> <span>while (n++ < end) { </span> <span>var k = Math.sqrt(n); </span> <span>var found = false; </span> <span>for (var i = 2; !found && i <= k; ++i) { </span> found <span>= n % i === 0; </span> <span>} </span> <span>if (!found) { </span> <span>postMessage(n.toString()); </span> <span>} </span> <span>} </span><span>})(2, 1e10); </span>
Wir können auch eine Funktion mit Reinigungslogik aus der beobachtbaren Erstellungsfunktion zurückgeben.
Abonnieren des Beobachtbaren ist unabhängig von der Art von Beobachtbar. Für beide Typen können wir drei Funktionen bereitstellen, die die grundlegende Anforderung der Benachrichtigungsgrammatik erfüllen, die aus OnNext, OnError und On -Completed besteht. Der Onnext -Rückruf ist obligatorisch.
<span>var worker = new Worker('prime.js'); </span>worker<span>.addEventListener('message', function (ev) { </span> <span>var primeNumber = ev.data * 1; </span> <span>console.log(primeNumber); </span><span>}, false); </span>
Als bewährte Verfahren sollten wir das Abonnement mit der Entsendungsmethode beenden. Dadurch werden alle erforderlichen Bereinigungsschritte durchgeführt. Andernfalls kann es möglich sein, zu verhindern, dass die Müllsammlung nicht verwendete Ressourcen aufräumt.
ohne abonnieren, das in der Variablen beobachtbare beobachtbare ist nur ein kaltes beobachtbares. Trotzdem ist es auch möglich, sie mit der Veröffentlichungsmethode in eine heiße Sequenz (d. H. Wir führen ein Pseudo -Abonnement durch) um.
.<span>(function (start<span>, end</span>) { </span> <span>var n = start - 1; </span> <span>while (n++ < end) { </span> <span>var k = Math.sqrt(n); </span> <span>var found = false; </span> <span>for (var i = 2; !found && i <= k; ++i) { </span> found <span>= n % i === 0; </span> <span>} </span> <span>if (!found) { </span> <span>postMessage(n.toString()); </span> <span>} </span> <span>} </span><span>})(2, 1e10); </span>
Einige der in RXJs enthaltenen Helfer befassen sich nur mit der Konvertierung vorhandener Datenstrukturen. In JavaScript können wir zwischen drei von ihnen unterscheiden:
Letzteres ist neu mit ES6 und kann durch Arrays (obwohl dies ein schlechter Ersatz ist und als einzelner Wert behandelt werden sollte) für ES5 oder älter.
rxjs bringt nun einen Datentyp zur Bereitstellung asynchroner Mehrwertunterstützung (Rückgabe). Daher sind die vier Quadranten jetzt ausgefüllt.
Während Iteratoren gezogen werden müssen, werden die Werte von Observablen gedrückt. Ein Beispiel wäre ein Ereignisstrom, bei dem wir das nächste Ereignis nicht erzwingen können. Wir können nur warten, um durch die Ereignisschleife benachrichtigt zu werden.
<span>var worker = new Worker('prime.js'); </span>worker<span>.addEventListener('message', function (ev) { </span> <span>var primeNumber = ev.data * 1; </span> <span>console.log(primeNumber); </span><span>}, false); </span>
Die meisten Helfer, die Observablen erstellen oder mit dem Umgang mit einem Scheduler erstellen oder mit dem Start eines Abonnements steuern, und wenn Benachrichtigungen veröffentlicht werden. Wir werden hier nicht auf Details eingehen, da der Standardplaner für die meisten praktischen Zwecke gut funktioniert.
Viele Operatoren in RXJs führen zu Parallelität wie Gas, Intervall oder Verzögerung. Wir werden uns nun einen weiteren Blick auf die vorherigen Beispiele werfen, in denen diese Helfer unerlässlich werden.
Schauen wir uns zuerst unseren Prime -Number -Generator an. Wir wollten die Ergebnisse über eine bestimmte Zeit zusammenhängen, so dass die Benutzeroberfläche (insbesondere am Anfang) nicht mit zu vielen Updates zu tun hat.
Hier möchten wir vielleicht die Pufferfunktion von RXJs in Verbindung mit dem zuvor erwähnten Intervallhelfer verwenden.
Das Ergebnis sollte durch das folgende Diagramm dargestellt werden. Die grünen Blobs entstehen nach einem bestimmten Zeitintervall (angegeben durch die Zeit, die zum Konstruktionsintervall verwendet wird). Ein Puffer aggregiert alle gesehenen blauen Blobs während eines solchen Intervalls.
Darüber hinaus können wir auch eine Karte einführen, die uns hilft, Daten zu transformieren. Zum Beispiel möchten wir die empfangenen Ereignisargumente möglicherweise um die übertragenen Daten als Zahl verwandeln.
<span>var observable = Rx.<span>Observable</span>.create(function (observer) { </span> observer<span>.onNext(42); </span> observer<span>.onCompleted(); </span><span>}); </span>
Die Fromevent -Funktion konstruiert ein beobachtbares aus jedem Objekt unter Verwendung des Standard -Ereignisemittermusters. Der Puffer würde auch Arrays mit einer Länge von Null zurückgeben, weshalb wir die Funktionsweise der Funktion vorstellen, um den Strom auf nicht leere Arrays zu reduzieren. Schließlich interessieren wir uns in diesem Beispiel nur an der Anzahl der generierten Primzahlen. Daher kartieren wir den Puffer, um seine Länge zu erhalten.
Das andere Beispiel ist das Suchabfragefeld, das nur nach einer bestimmten Leerlaufzeit gestartet werden sollte, um nur Anfragen zu starten. Es gibt zwei Funktionen, die in einem solchen Szenario nützlich sein können: Die Gasfunktion ergibt den ersten Eintrag in einem bestimmten Zeitfenster. Die Dbarl -Funktion ergibt den letzten Eintrag in einem bestimmten Zeitfenster. Die Zeitfenster werden auch entsprechend verschoben (d. H. Relativ zum ersten / letzten Element).
Wir wollen ein Verhalten erreichen, das sich in der folgenden Abbildung widerspiegelt. Daher werden wir den Entlüftungsmechanismus verwenden.
Wir möchten alle vorherigen Ergebnisse wegwerfen und nur den letzten erhalten, bevor das Zeitfenster erschöpft ist. Unter der Annahme, dass das Eingabefeld die ID -Abfrage hat, können wir den folgenden Code verwenden:
<span>(function (start<span>, end</span>) { </span> <span>var n = start - 1; </span> <span>while (n++ < end) { </span> <span>var k = Math.sqrt(n); </span> <span>var found = false; </span> <span>for (var i = 2; !found && i <= k; ++i) { </span> found <span>= n % i === 0; </span> <span>} </span> <span>if (!found) { </span> <span>postMessage(n.toString()); </span> <span>} </span> <span>} </span><span>})(2, 1e10); </span>
In diesem Code ist das Fenster auf 300 ms festgelegt. Außerdem beschränken wir Abfragen für Werte mit mindestens 3 Zeichen, die sich von früheren Abfragen unterscheiden. Dies beseitigt unnötige Anfragen nach Eingängen, die gerade korrigiert wurden, indem Sie etwas eingeben und löschen.
Es gibt zwei entscheidende Teile in diesem gesamten Ausdruck. Einer ist die Transformation des Abfragetextes in eine Anforderung mit der Suche, die andere ist die Funktion Switch (). Letzteres nimmt jede Funktion, die verschachtelte Observablen zurückgibt und Werte nur aus der neuesten beobachtbaren Sequenz erzeugt.
Die Funktion zum Erstellen der Anforderungen kann wie folgt definiert werden:
<span>var worker = new Worker('prime.js'); </span>worker<span>.addEventListener('message', function (ev) { </span> <span>var primeNumber = ev.data * 1; </span> <span>console.log(primeNumber); </span><span>}, false); </span>
Beachten Sie das verschachtelte Beobachtbare (das für ungültige Anfragen zu undefiniert sein könnte). Deshalb verkettet wir Switch () und wo ().
rxjs macht reaktive Programmierung in JavaScript zu einer freudigen Realität. Als Alternative gibt es auch Bacon.js, was ähnlich funktioniert. Trotzdem ist RX selbst eines der besten Dinge an RXJS, das auf vielen Plattformen erhältlich ist. Dies macht den Übergang zu anderen Sprachen, Plattformen oder Systemen ganz einfach. Es vereint auch einige der Konzepte der reaktiven Programmierung in einer Reihe von Methoden, die präzise und komponierbar sind. Darüber hinaus gibt es mehrere sehr nützliche Erweiterungen, wie z.
Wo sehen Sie Rxjs glänzen? Wie gehe ich mit Fehlern in RxJs um? Der CatchError -Operator fängt den Fehler auf der Quelle beobachtbar und setzt den Stream mit einem neuen beobachtbaren oder einem Fehler fort. Der Wiederholungsbetreiber beschreibt erneut die Quelle, die beobachtet werden kann, wenn er fehlschlägt. "," filter "," conat "," reduzieren "usw. Es gibt Dutzende von Betreibern in RXJs, die verwendet werden können, um komplexe Manipulationen von Sammlungen zu bewältigen, unabhängig davon, ob es sich um Arrays von Elementen, Ereignisströmen oder sogar Versprechen handelt.
Wie kann ich meinen RXJS -Code testen? Sie können auch Marmordiagramme zur Visualisierung von Observablen während des Tests verwenden.
Wie kann ich mich von einem Beobachtbaren abmelden? Wenn Sie ein beobachtbares Abonnement abonnieren, erhalten Sie ein Abonnementobjekt. Sie können die METRSUBSCRIBE -Methode in diesem Objekt aufrufen, um das Abonnement zu stornieren und keine Daten zu empfangen. multikastiert für viele Beobachter. Im Gegensatz zu einfachen Observablen behalten die Probanden eine Registrierung vieler Zuhörer bei.
Das obige ist der detaillierte Inhalt vonEinführung in die funktionale reaktive Programmierung mit RXJs. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!