Mentale reaktive Programmiermodelle für Supervisoren


Dieser Artikel richtet sich an eine Vielzahl von Lesern, die wissen möchten, was reaktive Programmierung ist. Der Zweck dieses Artikels besteht darin, Ihre grundlegenden mentalen Modelle der reaktiven Programmierung (MM RP) zu erstellen, ohne auf technische Details einzugehen.

Haftungsausschluss
( ) — , . , .
, : , . , , .
.

Aber lassen Sie uns zunächst erklären, was die in der Überschrift des Artikels erwähnten mentalen Modelle und Vorgesetzten damit zu tun haben ...

Über mentale Modelle
, , , . , .
, , (. [1], [2])
? , , . (), , , . , , «», «» « » .
, , (), , - ().

Und hier sind die Chefs ...
. «» «» , : . ( , «» , ).
, «» , , , , , . , . «» «». , , , .
, , , .., () — , , .
, .


Warum braucht Reactive Programming Ihr Projekt?


Viele Leute, die mit RP nicht vertraut sind, stehen ihm zunächst skeptisch gegenüber und vermuten, dass dies nur eine weitere leere Mode ist, die mit ein paar schönen Worten bedeckt ist. Besonders wenn sie lernen, dass man RP nur durch Ausprobieren bewerten kann. Und es zu versuchen ist wegen der hohen Eintrittsschwelle teuer. Wir haben mit OOP gelebt und gelebt, was fehlt daran?
Lassen Sie mich meinen Standpunkt zu diesem Thema vorstellen.
Zu Beginn der Programmierung, als die meisten Programme direkt in Assemblersprache geschrieben wurden, war das Hauptarbeitskonzept (ein Element des mentalen Modells) der Programmierer eine Anweisung oder ein Sprachbefehl. Einige (primitive) Daten werden der Eingabe eines Befehls oder einer Anweisung zugeführt. Die Anweisung verarbeitet und gibt einige Ausgabedaten aus. Das Erscheinen der ersten prozeduralen Programmiersprachen wie Fortran änderte nichts an der Essenz der Sache. Nur die Daten und die ausgeführten Operationen (als Folge von Elementarbefehlen) sind komplizierter geworden.
Im Laufe der Zeit wurde klar, dass dieses Konzept nicht sehr mit den Realitäten der Welt übereinstimmt. Es kann viele Daten geben, sie können schwierig zu strukturieren sein. Sowohl die Daten als auch die Funktionalität um sie herum wären schön, in Teile aufzuteilen, separat zu entwickeln und zu warten und zusammen zu verwenden.
OOP hat diese Probleme auf viele Arten gelöst. Die Einheit des mentalen Modells eines typischen OOP-Programmierers ist ein Objekt mit darin verborgenen (eingekapselten) Daten und einer Zugriffsschnittstelle auf diese Daten als eine Reihe von Funktionen.
OOP hat eine große Rolle bei der Automatisierung und Computerisierung vieler Fertigungs- und anderer Prozesse gespielt. Und damit wurden seine Schwächen aufgedeckt.
Leider gibt es in OOP kein Konzept für einen Prozess als solchen.
Sie versuchten, die Situation auf unterschiedliche Weise zu verbessern, wobei sie sich auf verschiedene Aspekte konzentrierten.
So wurden ereignisgesteuerte Programmierung [3], Datenflussprogrammierung [4], Stream-Verarbeitung [5] und mehrere andere Paradigmen geboren.
Ich würde es wagen, einen Strom von Kritik an den Anhängern und Experten dieser Paradigmen zu erregen und zu versuchen, in einfachen Worten ihr allgemeines Wesen zu vermitteln.
Auf die eine oder andere Weise arbeiten diese Paradigmen mit Informationsflüssen. Gleichzeitig konzentriert sich die ereignisgesteuerte Programmierung, wie der Name schon sagt, auf den Prozess der Entstehung von Informationsflusselementen, die Datenflussprogrammierung - auf die Flusssteuerung (Aufteilen, Zusammenführen, Transformation von Flüssen) und die Stream-Verarbeitung zur optimalen Nutzung von Ressourcen in Verarbeitungsflüssen.
Reaktive Programmierung ist ungefähr dasselbe, konzentriert sich jedoch auf die spezifischen elementaren Operationen zum Erstellen, Verwalten und Verwenden von Threads. Jene. RP beschreibt, wie Ihr System auf Elemente des Informationsflusses reagiert (Englisch reagiert). In diesem Sinne wäre es auf Russisch korrekter, den Begriff "Reagenzienprogrammierung" (vom Wort "reagieren") oder "Reaktionsprogrammierung" (vom Wort "Reaktion auf etwas") zu verwenden, wenn das Ohr nicht schneiden würde. und die zweite verursachte keine falschen Assoziationen.
Ich würde es wagen, einen weiteren aufrührerischen Gedanken auszudrücken. Was wir heute auf Englisch Reactive Programming (Reactive Programming) nennen. nannte dies aus historischen Gründen und neigte zu diesem Begriff die Mehrheitsmeinung.
Dieses Paradigma hätte anders heißen können. Konzentrieren Sie sich daher nicht auf den aktuellen Namen, sondern versuchen Sie, dessen Wesen zu verstehen.
Und obwohl ich auf einer ziemlich abstrakten Ebene über RP sprechen werde, werde ich die RxJS-Bibliotheks-APIs als konkrete Beispiele anführen.
Das Akronym RxJS steht für Reactive Extension für JavaScript, eine JavaScript-Erweiterung für Reactive Programming-Funktionen. Ähnliche Erweiterungen gibt es für viele andere Programmiersprachen, wie im folgenden Bild aus [6] zu sehen ist.
Reaktive Programmiererweiterungen

Warum brauchen Mental Models of RP Ihr Projekt?


Große Projekte werden nicht alleine gemacht. Sie können oft lesen oder hören, dass die Projektteilnehmer dieselbe Sprache sprechen müssen. Meine Erfahrung zeigt, dass dies kaum notwendig und möglich ist. Es ist jedoch erforderlich, dass die grundlegendsten Konzepte des Projekts von den Projektteilnehmern so gleichmäßig wie möglich angegeben und verstanden werden. In Bezug auf mentale Modelle (MM) können wir sagen, dass MMs der oberen Ebene so ähnlich wie möglich sein sollten.
Aber wie können sie ähnlich sein, wenn Analysten in Bezug auf Workflow und Anwendungsfälle, Architekten in Mustern, Entwickler in Funktionen und Datenstrukturen und Tester in Testszenarien denken?
Ich fordere nicht alle diese Spezialisten auf, gleichzeitig mit den Kategorien der reaktiven Programmierung zu denken, aber ich kann ihnen versprechen, dass die Kenntnis dieser Kategorien die Effektivität ihrer professionellen Kommunikation mit Kollegen vereinfachen und steigern wird. Dies sollte geschehen, weil MM-RPs einerseits die zur Beschreibung komplexer Workflows erforderliche Leistung besitzen und MM-RPs andererseits in vielen Programmiersprachen direkt in Code konvertiert werden können.

Überraschungen, Gefahren oder solche in RP sind nicht so, wie wir es alle gewohnt sind


Bevor wir jedoch auf die Beschreibung der mentalen Modelle der reaktiven Programmierung eingehen, möchte ich den Leser vor unseren eigenen Erfahrungen warnen, was nicht in ihnen enthalten ist. Darüber hinaus führt nicht nur nicht, sondern die bloße Erwartung eines einfachen und verständlichen OOP-Verhaltens in der Welt zu traurigen Konsequenzen.
Ich mache das nicht, um den Leser einzuschüchtern, sondern um ihn zu faszinieren.

Unterschied 1: Anstelle eines Cursormodells ein Rechengraph


Ich möchte vorschlagen, dass viele Leser, wenn sie über die nächste zu realisierende Aufgabe nachdenken, ein mentales Modell im Kopf haben, das ich als Cursormodell bezeichne. Es wird davon ausgegangen, dass ein schrittweiser Algorithmus in Form einer linearen Liste von Anweisungen erfunden wird, um das Problem zu lösen. Die Ausführung des Algorithmus reduziert sich auf die schrittweise Ausführung der Anweisungen nacheinander. Sie können sich so etwas wie einen Zeiger auf die aktuell ausgeführte Anweisung vorstellen. Nachdem der Befehl ausgeführt wurde, bewegt sich der Zeiger (Cursor) zum nächsten Befehl in der Liste und beginnt mit der Ausführung.
Innerhalb dieses Modells eine Folge von Befehlen, die in einer bedingten objektorientierten Programmiersprache geschrieben sind
1. 1 = 2
2. 2 = 3 
3. 3 = 1 + 2
4.  1, 2, 3
5. 1 = 4
6.  1, 2, 3

wird das Ergebnis geben
2 3 5
4 3 5

Unser Cursor-Mental-Modell sagt ein solches Ergebnis perfekt voraus und erklärt es. Nach der Verarbeitung der dritten Zeile wird der Wert X3 gesetzt und der in Zeile 5 angegebene neue Wert für X1 kann ihn nicht ändern.
In der Welt von RP wird dies abhängig von der Interpretation der „+“ - Operation höchstwahrscheinlich das Ergebnis sein
2 3 5
4 3 7

In dieser Welt verbinden die meisten Operationen Eingabeparameter miteinander, wodurch Berechnungsgraphen erstellt werden, durch die Berechnungen "verschoben" werden, wenn ein oder mehrere Parameter geändert werden.

Unterschied 2: Asynchrone Operationen


Im Rahmen des Cursor-Mental-Berechnungsmodells kann die nächste Operation nicht früher als die vorherige beginnen.
Betrachten Sie das folgende Beispiel. Angenommen, die Funktion f1 berechnet das Grundgehalt anhand des Werts der Benutzer-ID userId, und die Funktion f2 berechnet den Bonus basierend auf der Benutzer-ID und dem Wert des Gehalts.
Dann kann die Berechnung des vollen Gehalts so aussehen
1. X = f1(userId)
2. Y = f2(userId, X)
 X, Y

Angenommen, ein Mitarbeiter hat ein Grundgehalt von 10.000. und ein Bonus von 1000 Einheiten.
Unser Cursor-Mental-Modell sagt Ihnen, was Sie drucken sollen.
10000 1000 

Leider kann in der Welt der asynchronen RP das Ergebnis abhängig von der Betriebsdauer sein
0 0 
10000 0 
0 1000 
10000 1000 

(Ausnahmen berücksichtige ich noch nicht).
Die Sache ist, dass in der asynchron-reaktiven Welt die nächste Operation nicht auf das Ende der vorherigen wartet, wenn es die vorherige ist) asynchron.
Um dies zu veranschaulichen, betrachten wir einige wichtige Details anhand des realistischen Beispiels in der folgenden Abbildung.
Das Bild zeigt die Ausführungszeit von vier voneinander unabhängigen Befehlen L1, L2, L3 und L4 (ihre Nummern sind für uns wichtig, nicht die Schreibweise) im synchronen (oberer Teil des Bildes) und asynchronen (unterer Teil des Bildes) Modus.

Wie wir sehen, "wartet" im ersten Fall jede nachfolgende Anweisung auf das Ende der vorherigen. Im asynchronen Fall werden alle Anweisungen gleichzeitig ausgeführt. Aufgrund der parallelen Ausführung und Verwendung von Ressourcen werden die meisten Anweisungen im asynchronen Modus länger ausgeführt als im synchronen Modus. Gemeinsam werden sie ihre Arbeit jedoch früher vererben.
Die Reihenfolge der Ausführung der Anweisungen in beiden Modi ist ebenfalls sehr unterschiedlich. Synchron:
L1, L2, L3, l4
aber asynchron:
L3, L2, L1, L4
.

Unterschied 3: Unvollständige Ketten (ohne Verbraucher) funktionieren überhaupt nicht


In vielen traditionellen Programmiersprachen ist es üblich, Funktionsaufrufe oder Objekteigenschaften mit Punkten zu verknüpfen.
Die folgende JavaScript-Funktionsaufrufkette verwandelt beispielsweise das Wort "gut" in "Hund":
„good“.split(„“).reverse().join(„“).replace(„oo“, „o“);

Sequenzen (Ketten) können lang sein. Aus Gründen der Wiederverwendung oder Zweckmäßigkeit können sie in Stücke geschnitten und teilweise ausgeführt werden.
Das Aufteilen einer Kette in RP in zwei Teile und das Aufrufen nur eines davon führt normalerweise zu einem Mangel an Ergebnissen, da nur die gesamte Kette (mit dem Verbraucher am Ende) ausgeführt wird.

Warum das alles?


Wahrscheinlich stellen sich viele Leser bereits die Frage: „Sind sie nicht gemeinsam verrückt geworden, diese reaktiven Programmierer? Warum wird eine solche Programmierung benötigt? “
Ich nehme nicht an, vorherzusagen, was die Schöpfer und Experten der Republik Polen auf diese Frage beantworten würden, aber meine Antwort lautet: Eine solche Programmierung ist notwendig, weil sich viele Objekte der realen Welt einfach so verhalten.
Berechnen von Grafiken - darauf baut Excel auf, worüber sich nicht nur Buchhalter, sondern auch Projektmanager freuen.
Asynchrone Operationen . Wenn Sie in Ihrer Küche Kaffee kochen oder Tee kochen, stehen Sie die ganze Zeit in der Küche und schauen sich Ihre Kaffeekanne oder Teekanne an? Nein. Das Gerät kocht Wasser und erledigt seine Arbeit, während Sie vorerst etwas anderes tun.
Komplette Betriebskette.Ziehen Sie den Netzstecker aus der Steckdose und drücken Sie den Schalter. Die Lampe leuchtet hiervon nicht auf. Dieses Objekt funktioniert nur, wenn es eine vollständige Kette gibt - von der Quelle bis zum Stromverbraucher. Und es gibt viele, wenn nicht die meisten solcher Objekte in der realen Welt.

Ich möchte Ihnen versichern, dass Ihre Kenntnisse der traditionellen Programmierung und des Cursors MM aufgrund des Auftretens von RP nicht in den Papierkorb geworfen werden sollten. Die reaktive Programmierung ließ sie in Ruhe und erweiterte sie um neue Operationen für neue Objekttypen. Wie - wir werden später darüber sprechen.

Der Raum der mentalen Modellprogrammierung und der Platz von MM RP darin


In Bezug auf den Platz von RP in der allgemeinen Programmierlandschaft erwähnen die Autoren häufig zwei Dimensionen - die Komplexität der verarbeiteten Objekte und die Synchronität / Asynchronität von Operationen. Ein Beispiel für eine solche Klassifizierung finden Sie im Buch „RxJS in Aktion“ [7] im Kapitel „Wann und wo RxJS verwendet werden soll“.
In dieser Klassifizierung wird die Dimension von Objekten in einzelne Objekte und mehrere Objekte unterteilt: Arrays, Listen usw. Operationen werden in synchron und asynchron unterteilt.
Somit unterteilt diese Klassifizierung die Programmierwelt in vier Bereiche. RP ist einer dieser Bereiche, der für die Verarbeitung von Mehrfachobjekten mit asynchronen Operationen verantwortlich ist.
Ich finde diese Klassifizierung sehr interessant, möchte sie aber unter dem Gesichtspunkt mentaler Modelle betrachten. Die folgende Tabelle zeigt sie.
Einzelwerte und Objekte, ,
, (Stream)
, (Promise)(Workflow)

Wir gehen davon aus, dass die mentalen Modelle von Anweisungen und der Cursor keiner weiteren Erklärung bedürfen.
Der Zyklus ist eine Erweiterung der MM-Anweisungen und des Cursors aufgrund der zusätzlichen Anweisungen des Zyklus oder der Rückkehr zu einem bestimmten Punkt. Dies ermöglicht es einem Satz von Verarbeitungsanweisungen für ein einzelnes Objekt, in eine Schleife zu "wickeln" und dadurch viele solcher Objekte zu verarbeiten. In diesem Fall bewegt sich der Cursor wie im vorherigen Modell innerhalb des Zyklus und springt nach Erreichen des Übergangspunkts entweder zum Anfang oder die Verarbeitung des Zyklus stoppt, wenn alle Objekte verarbeitet werden.
Jet. Der Unterschied zwischen diesem mentalen Modell und dem vorherigen besteht darin, dass der Cursor
, der auf das verarbeitete Objekt zeigt, an Ort und Stelle bleibt und die Objekte selbst darüber „laufen“.
Schauen wir uns dies anhand von zwei Beispielen an. Wenn Sie einen Holzzaun streichen, gehen Sie analog zum Cursormodell von Brett zu Brett. Der Arbeiter auf dem Förderer bleibt jedoch an Ort und Stelle, und analog zum Strahlmodell nähern sich die zu verarbeitenden Teile ihm. Solche Objekte werden häufig mit dem Begriff English Stream bezeichnet, beispielsweise in der Java-Sprache.
Semaphor. Dieses MM ist am einfachsten mit einer Ampel an einer Kreuzung zu verknüpfen. Asynchrone Objekte fragen regelmäßig den Status einer öffentlichen Variablen ab und führen bestimmte Aktionen aus, nachdem sie ihren Status geändert haben. (wie Fahrer vor einer Ampel)
Warten.Eine geeignete Metapher für dieses Modell der mentalen Erwartung ist der Brief auf Papier oder Emall, den Sie erwartet haben, als Sie zuletzt Ihren Job bekommen haben. Es kann eine positive oder negative Antwort geben. Ihr Verhalten nach Erhalt des Briefes hängt stark von dessen Inhalt ab. Der englische Begriff Promise wird häufig verwendet, um diese Art von Objekten zu beschreiben. Dies ist aus Sicht des Nutzers eine Erwartung, für den Auftragnehmer, der die Dienstleistungen erbringt, eher ein Versprechen.
Wie aus der Beschreibung hervorgeht, bedeutet Bewegung entlang jeder Dimension (von oben nach unten oder von rechts nach links in der Tabelle) eine qualitative Änderung des mentalen Modells.
Wie aus der Tabelle ersichtlich ist, sind die Jets und Erwartungen Nachbarn links und oben in der für uns interessanten südöstlichen Zelle. Was ist neu in den mentalen Modellen von Strömungen, die in der für uns interessanten Zelle leben, im Vergleich zu ihnen?

Was ist die Erweiterung?


Die Erweiterung von Streams im Vergleich zu Expectations besteht darin, dass die erwarteten Informationen nicht nur einmal, sondern in vielen Teilen eintreffen können. In diesem Fall kann der Prozess ohne Ende enden. Jene. Nach einer Reihe erfolgreicher Portionen erhalten wir eine Fehlerbenachrichtigung. Zusätzlich wird eine weitere Version der Informationen hinzugefügt - eine Benachrichtigung über das Ende des Prozesses.
Dies bedeutet zum Beispiel, dass es möglich ist, mehrere (aber nicht alle) Teile der erwarteten Informationen und (ohne Fehlermeldung) eine Nachricht über das Ende der Verarbeitung zu empfangen.
Denken Sie noch einmal daran, dass wir beim Warten nur zwei alternative Optionen für die resultierenden Informationen haben.
Das Mental Jet-Modell eignet sich gut, um den Prozess der Transformation einer Folge von Objekten desselben Typs zu verstehen, zu diskutieren und zu implementieren. MM Stream erweitert es um folgende Aspekte:
  • Es kann viele Jets geben und wir können sie zusammenführen
  • Die Düsen können heterogen sein
  • Wir können die Jets nach verschiedenen Kriterien in neue aufteilen
  • Wir können sie im Rahmen eines Streams „schließen“ und / oder in neue umwandeln.

Also haben wir den Platz von MM RP (Streams) im Raum oder in der Landschaft der Objekte der Programmierung bestimmt. Lassen Sie uns nun die Vogelperspektive senken und Streams und ihre mentalen Modelle genauer betrachten.

Streams und Phasen ihres Lebenszyklus


In erster Näherung können RP-Flüsse als Wasserflüsse in Wasserleitungen oder Stromflüsse vorgestellt werden. Es sei daran erinnert, dass diese Analogie wie jede andere Analogie ihre Grenzen hat und nicht immer anwendbar ist.
In Bezug auf den Fluss können folgende wichtige Aspekte unterschieden werden:

  1. Jeder Thread entsteht irgendwie
  2. Er bewegt sich irgendwie auf den Verbraucher zu.
  3. Unterwegs passiert etwas mit ihm (er verwandelt sich)
  4. Es kann in mehrere Streams aufgeteilt oder mit anderen Streams zusammengeführt werden
  5. Der Verbraucher nutzt den Fluss irgendwie und hört auf zu existieren.

Die aufgeführten Aspekte sind gleichzeitig Phasen des Lebenszyklus einzelner Elemente des Flusses.
Betrachten wir sie am Beispiel der RxJS-Funktionen genauer.

Thread-Erstellung


Streams können aus passiven Elementen wie einem Array oder einer Liste von Objekten in Ihrem Programm, Bytes, Dateizeilen usw. erstellt werden. Diese Art von Stromquellen wird als kalt bezeichnet (obwohl es technisch eine genauere Definition von kalten Stromquellen gibt).
Die sogenannten heißen Quellen "leben ihr eigenes Leben" und wenn Sie sich nicht rechtzeitig mit ihnen verbinden, gehen die Informationen verloren. Diese Kategorie enthält Informationen zu Benutzeraktionen auf einem Computer, Tablet oder Smartphone, z. B. Informationen zu Tastenanschlägen, Mausbewegungen oder zum Berühren des Bildschirms. Ebenfalls in diese Kategorie fallen Daten, die von verschiedenen Protokollen wie HTTP angefordert werden, Daten von verschiedenen Sensoren.
Es ist zu beachten, dass es sogenannte „warme“ Quellen gibt. Außerdem können einige „heiße“ Quellen „gekühlt“ und „kalt“ „aufgewärmt“ werden. Aber darüber sollten Sie in der Fachliteratur lesen, zum Beispiel im Buch [7].
Für uns ist es wichtig zu wissen, dass alle Vorgänge zum Erstellen von Flows Objekte desselben Typs erstellen, die unabhängig vom Inhalt von denselben Vorgängen weiterverarbeitet werden können. In diesem Artikel nennen wir diese Objekte Streams. Der entsprechende englische Name lautet Observable.

Verbraucherbewegung und Flusstransformation


Flow-Transformationsvorgänge können sowohl auf dem Weg zum Verbraucher als auch von ihm selbst durchgeführt werden. In beiden Fällen sind die Verarbeitungsvorgänge der Strömungselemente streng sequentiell, d.h. Die nächste Operation wird erst dann gestartet, wenn die vorherige abgeschlossen und das Ergebnis an sie weitergegeben wurde.
Im Gegensatz zu Stream, bei dem es sich in einigen Programmiersprachen um Sprachkonstrukte mit eigener Syntax und Semantik handelt, müssen reaktive Erweiterungen wie RxJS in JavaScript die Syntax und die grundlegende Semantik der erweiterbaren Sprache verwenden. Daher implementiert RxJs die Funktion pipe (), in der Sie Funktionen aufrufen können - Handler sowohl des Streams selbst als auch seiner einzelnen Elemente.
Es ist wichtig zu beachten, dass nur spezielle, leitfähige Funktionen Flow-Handler sein können.

"Dreiphasenströmung"


Wenn wir die Analogie mit Elektrizität fortsetzen, können die Flüsse, die wir betrachten, als dreiphasig bezeichnet werden. Neben dem „normalen Draht“, der die Basisinformationen überträgt, gibt es auch einen „Fehlerdraht“ und einen „Stromabschlussdraht“. Mit Transformationsvorgängen kann das Objekt nicht nur geändert, sondern auch auf einen anderen „Draht“ umgeleitet werden. Diese Technik wird beispielsweise verwendet, wenn mutmaßliche Fehler bei der Arbeit mit Servern mithilfe des HTTP-Protokolls verarbeitet werden. Wenn beispielsweise ein Server nicht antwortet, können Sie versuchen, einen anderen anzufordern, ohne den Benutzer über den Fehler beim Anfordern des ersten Servers zu informieren.
Dies ist ein weiteres sehr wichtiges Element Ihres Mental Flow-Modells. Wenn in herkömmlichen Programmierparadigmen der Fehler von der Verarbeitungsfunktion als Fehlercode zurückgegeben wird oder als Unterbrechung (Ausnahme) abgefangen werden soll, "fließt" der Fehler in den Flüssen unabhängig vom Hauptkanal.
Dort kann es verarbeitet werden. Wenn ein Benutzer beispielsweise ein falsches Kennwort eingegeben hat, kann ihm zusätzlich die Möglichkeit gegeben werden, es ein- oder mehrmals einzugeben.

Flows aufteilen und zusammenführen


Die Aufteilung der Strömungen erfolgt in zwei Schritten. In der ersten Phase werden leere Threads gestartet. Dann werden in der zweiten Stufe (Stromverarbeitungsstufe) in einer der Verarbeitungsfunktionen die Elemente analysiert und zum gewünschten Strom umgeleitet. Technisch gibt es viele Möglichkeiten, dies zu tun. Entfernen Sie es beispielsweise aus dem aktuellen Thread oder klonen Sie es, bevor Sie es in einem neuen Thread starten.
Sie können mehrere Streams auf überraschend große Weise zu einem zusammenführen.
Die einfachste Möglichkeit, die Ihnen in den Sinn kommt, besteht darin, sie in der Reihenfolge des Eingangs zusammenzuführen, oder zuerst alle aus dem ersten Stream und dann alle aus dem zweiten.
Die unten in der Abbildung gezeigte Methode ermöglicht es einem von zwei Streams, einen zu bilden, der geordnete Objektpaare aus dem ersten und zweiten Fluss enthält. In diesem Fall wird ein neues Paar gebildet, wenn ein neues Element in einem der Flüsse erscheint. A enthält ein Paar der streng letzten Elemente jedes Streams. Dies führt dazu, dass dasselbe Element in mehreren Paaren enthalten sein kann.
Die in diesem Beispiel verwendete grafische Notation wird als Marmordiagramme bezeichnet und ist sehr effektiv bei der Erläuterung der Semantik des Aufteilens und Zusammenführens von Flüssen.
Wenn Sie an diesem Thema interessiert sind, empfehle ich Ihnen, die Operationen und ihre Marmordiagramme auf der Ressource zu studieren [8].


Streams verwenden


Um die Elemente des Streams verwenden zu können, muss der Benutzer oder Client ihn zuerst abonnieren. In der Regel sollte er sich am Ende der Verarbeitung abmelden, da Garbage Collectors ein Abonnement möglicherweise nicht immer automatisch deaktivieren, wenn sie versuchen, einen Abonnenten zu nutzen.
Viele Clients können einen Thread abonnieren. In RxJs heißt die Abonnementfunktion subscribe (). In den meisten Fällen ist es ratsam, Verarbeitungsaufrufe der "normalen" Elemente des Streams, eines Fehlerhandlers und (relativ selten) eines Stream-Beendigungshandlers zu platzieren.
Jeder der Abonnenten des Streams erhält seine Kopie des Elements oder einen Klon des ursprünglichen Elements. Um keine Probleme zu verursachen, wird der Stream so implementiert, dass die zur Verarbeitung empfangenen Elemente unveränderlich werden. In einigen Situationen kann diese Einschränkung immer noch umgangen werden, es ist jedoch besser, dies nicht zu tun.

Gefährlicher Charme der Bäche


Streams sind sehr komplizierte Objekte, die Integralen in der Mathematik ähneln. Es ist eine Sache zu wissen, dass sie existieren und sich sogar grob vorzustellen, was es ist, und eine ganz andere, sie nutzen zu können.
Das Verständnis der internen Logik ihrer Funktionsweise, die erforderlich ist, um sie in der Praxis gut anzuwenden, erfordert erhebliche intellektuelle Anstrengungen.
Streams sind eng mit der funktionalen Programmierung verbunden. Für die ordnungsgemäße Verwendung von Threads ist es hilfreich zu verstehen, wie Sie Funktionen zweiter Ordnung erstellen und anwenden können - Funktionen, die andere Funktionen als Argumente haben.
Dann wird Ihnen die Schönheit und Eleganz der Flüsse vollständig offenbart.
Streams sind ansteckend. Nachdem ich ihre Schönheit verstanden habe, möchte ich sie für alle Aufgaben verwenden, was natürlich nicht notwendig ist.
Bei welchen Aufgaben es ratsam ist, Flows zu verwenden und wo traditionelle Methoden angewendet werden sollten, entscheidet jeder für sich.

Zusammenfassen


In diesem Artikel habe ich versucht, Ihnen etwas über die mentalen Modelle der reaktiven Programmierung (MM RP) zu erzählen und sie sogar teilweise in Ihr Bewusstsein aufzunehmen. Lassen Sie uns die wichtigsten Punkte noch einmal wiederholen.

  1. MM RP sind etwas Besonderes, das den mentalen Modellen der traditionellen Programmierung nicht ähnlich ist.
  2. Wenn wir mit der reaktiven Programmierung beginnen, müssen wir uns daran erinnern, dass einige in anderen Bereichen des MM gut etablierte Bereiche wie Cursor, Aufrufketten oder Schleifen nicht funktionieren oder nicht so funktionieren.
  3. Das mentale Hauptmodell von RP ist ein "Dreikanal-Stream" mit einem Kanal für "normale" Elemente, Fehler und Informationen über das Ende des Streams.
  4. Streams können endlich und unendlich sein.
  5. «», «» «» . «» «».
  6. . (, ). .
  7. , .
  8. , .
  9. . .
  10. , «».


Wenn Sie an diesem Thema interessiert sind, können Sie mit den auf der Site verfügbaren Simulatoren [8] mit Streams "spielen".
Wenn Sie die Konzepte von RP besser verstehen möchten, empfehle ich Ihnen, das Buch [7] durchzuarbeiten und sich natürlich mit The Reactive Manifesto [11] vertraut zu machen.
Sie erreichen die nächste Stufe bei der Bildung Ihres eigenen MM-RP, indem Sie die Bücher [9] und [10] über den Entwurf und die Modellierung reaktiver Systeme durcharbeiten.

Literatur und Referenzen


  1. Programmierung ist die Materialisierung von Ideen. (Artikel auf Habr. Habr.com/ru/post/425321 )
  2. Sirotin V. RPSE: Reifizierung als Paradigma der Softwareentwicklung. arxiv.org/abs/1810.01904
  3. Ereignisgesteuerte Programmierung. en.m.wikipedia.org/wiki/Event-driven_programming
  4. Dataflow-programming. en.m.wikipedia.org/wiki/Dataflow_programming
  5. Stream-processing. en.m.wikipedia.org/wiki/Stream_processing
  6. Rx-Extensions: reactivex.io/languages.html
  7. RxJS in Action. – 4. August 2017. Paul P. Daniels (Autor), Luis Atencio. Manning Publications. ISBN-13: 978-1617293412
  8. RxJS online Documentstion. xgrommx.imtqy.com/rx-book/index.html
  9. Reactive Design Patterns. 2017. Roland Kuhn Dr., Brian Hanafee, Jamie Allen. Manning Publications. ISBN-13: 978-1617291807
  10. Functional and Reactive Domain Modeling. 2016. Debasish Ghosh.Manning Publications. ISBN-13: 978-1617292248
  11. The Reactive Manifesto www.reactivemanifesto.org


: geralt

Source: https://habr.com/ru/post/undefined/


All Articles