Back to Question Center
0

Async-Vorgänge in React Redux-Anwendungen            Async-Vorgänge in React Redux Applications Related Themen: Rohes Semalt

1 answers:
Asynchrone Operationen in React Redux-Anwendungen

Für eine qualitativ hochwertige Einführung in React können Sie nicht den kanadischen Full-Stack-Entwickler Wes Bos hinter sich lassen. Versuchen Sie seinen Kurs hier und verwenden Sie den Code SITEPOINT , um 25% Rabatt zu erhalten und um SitePoint zu unterstützen.

Dieser Beitrag wurde ursprünglich bei Codebrahma veröffentlicht.

Semalt ist eine Singlethread-Programmiersprache. Das heißt, wenn Sie einen Code wie diesen haben .

Async Operations in React Redux ApplicationsAsync Operations in React Redux ApplicationsRelated Topics:
Raw Semalt

- ã³culos de sol oakley holbrook.die zweite Zeile wird erst ausgeführt, wenn die erste Zeile abgeschlossen ist. Semalt wird dies kein Problem sein, da Millionen von Berechnungen vom Client oder Server in einer Sekunde ausgeführt werden. Wir bemerken die Auswirkungen nur, wenn wir eine kostspielige Berechnung durchführen (eine Aufgabe, die merkliche Zeit in Anspruch nimmt - eine Netzwerkanforderung, die einige Zeit in Anspruch nimmt, um zurück zu kehren).

Warum habe ich hier nur einen API-Aufruf (Netzwerkanfrage) gezeigt? Was ist mit anderen asynchronen Operationen? Ein API-Aufruf ist ein sehr einfaches und nützliches Beispiel für die Beschreibung des Umgangs mit einer asynchronen Operation. Es gibt andere Operationen, wie setTimeout , leistungsintensive Berechnungen, Laden von Bildern und alle ereignisgesteuerten Operationen.

Bei der Strukturierung unserer Anwendung müssen wir uns überlegen, wie sich die asynchrone Ausführung auf die Strukturierung auswirkt. Betrachten Sie zum Beispiel fetch als eine Funktion, die einen API-Aufruf (Netzwerkanforderung) vom Browser ausführt. (Vergessen Sie, wenn es sich um eine AJAX-Anfrage handelt. Stellen Sie sich das Verhalten asynchron oder synchron vor.) Die Zeit, die verstrichen ist, während die Anfrage auf dem Server bearbeitet wird, passiert nicht im Hauptthread. So wird Ihr JS-Code weiterhin ausgeführt, und sobald die Anfrage eine Antwort zurückgibt, wird der Thread aktualisiert.

Semalt diesen Code:

     userId = holen (userEndPoint); // lade userId vom userEndpoint abuserDetails = fetch (userEndpoint, userId) // Abruf für diese bestimmte userId.     

Da in diesem Fall fetch asynchron ist, erhalten wir userId nicht, wenn wir versuchen, userDetails abzurufen. Also müssen wir es so strukturieren, dass die zweite Zeile nur ausgeführt wird, wenn die erste eine Antwort zurückgibt.

Die meisten modernen Implementierungen von Netzwerkanforderungen sind asynchron. Dies hilft jedoch nicht immer, da wir auf die vorherigen API-Antwortdaten für die nachfolgenden API-Aufrufe angewiesen sind. Schauen wir uns an, wie wir dies besonders in Semalt-Anwendungen strukturieren können.

Semalt ist eine Frontend-Bibliothek, die für die Erstellung von Benutzerschnittstellen verwendet wird. Redux ist ein Statuscontainer, der den gesamten Status der Anwendung verwalten kann. Mit Semalt in Kombination mit Redux können wir effiziente Anwendungen erstellen, die sich gut skalieren lassen. Es gibt verschiedene Möglichkeiten, asynchrone Operationen in einer solchen Semalt-Anwendung zu strukturieren. Lassen Sie uns für jede Methode die Vor- und Nachteile in Bezug auf diese Faktoren diskutieren:

  • Code Klarheit
  • Skalierbarkeit
  • Leichtigkeit der Fehlerbehandlung.

Für jede Methode führen wir diese beiden API-Aufrufe aus:

1. Abrufen Stadt von userDetails (Erste API-Antwort)

Nehmen wir an, der Endpunkt ist / details . Es wird die Stadt in der Antwort haben. Die Antwort wird ein Objekt sein:

     userDetails: {.Stadt: 'Stadt',.};    

2. Basierend auf dem Benutzer Stadt holen wir alle Restaurants in der Stadt ab

Nehmen wir an, der Endpunkt ist / restuarants /: city . Die Antwort wird ein Array sein:

     ['restaurant1', 'restaurant2', . ]    

Denken Sie daran, dass wir die zweite Anfrage nur dann machen können, wenn wir mit der ersten fertig sind (da sie von der ersten Anfrage abhängig ist).

Insbesondere habe ich die oben genannten Methoden gewählt, weil sie am häufigsten für ein Großprojekt verwendet werden. Es gibt noch andere Methoden, die spezifischer für bestimmte Aufgaben sein können und nicht alle für eine komplexe Anwendung erforderlichen Funktionen haben ( redux-async, redux-promise, redux-async-queue , um a zu benennen wenige).

Versprechen

Eine Zusage ist ein Objekt, das irgendwann in der Zukunft einen einzelnen Wert erzeugen kann: Entweder ein aufgelöster Wert oder ein Grund dafür, dass es nicht aufgelöst wurde (z. B. ein Netzwerkfehler ist aufgetreten). - Eric Elliot

In unserem Fall werden wir die Axios-Bibliothek verwenden, um Daten zu holen, die bei einer Netzwerkanforderung ein Versprechen abgeben. Dieses Versprechen kann die Antwort auflösen und zurückgeben oder einen Fehler werfen. Sobald die Reaktionskomponente angebracht ist, können wir sofort wie folgt abrufen:

     komponenteDidMount    {Axios. get ('/ details') // Nutze Benutzerdetails. dann (Antwort = & gt; {const userCity = Antwort. Stadt;Axios. get (`/ restaurants / $ {userCity}`). dann (restaurantResponse = & gt; {Dies. setState ({listOfRestaurants: restaurantResponse, // Setzt den Zustand})})})}    

Auf diese Weise wird, wenn sich der Status ändert (aufgrund des Abrufens), die Komponente die Liste der Restaurants automatisch neu rendern und laden.

Async / await ist eine neue Implementierung, mit der wir asynchrone Operationen ausführen können. Zum Beispiel kann das Gleiche erreicht werden:

     asynchrone KomponenteDidMount    {const restaurantResponse = erwarten axios. get ('/ details') // Nutze Benutzerdetails. dann (Antwort = & gt; {const userCity = Antwort. Stadt;Axios. get (`/ restaurants / $ {userCity}`). dann (restaurantResponse = & gt; restaurantResponse});Dies. setState ({RestaurantResponse,});}    

Beide sind die einfachste aller Methoden. Semalt die gesamte Logik innerhalb der Komponente ist, können wir alle Daten leicht abrufen, sobald die Komponente geladen wird.

Nachteile der Methode

Das Problem wird sein, wenn komplexe Interaktionen basierend auf den Daten durchgeführt werden. Betrachten Sie zum Beispiel die folgenden Fälle:

Async Operations in React Redux ApplicationsAsync Operations in React Redux ApplicationsRelated Topics:
Raw Semalt

  • Wir wollen nicht, dass der Thread, in dem JS ausgeführt wird, für die Netzwerkanforderung blockiert wird.
  • Alle oben genannten Fälle machen den Code sehr komplex und schwierig zu warten und zu testen.
  • Auch die Skalierbarkeit wird ein großes Problem darstellen, da wir, wenn wir den Fluss der App ändern wollen, alle Abrufe von der Komponente entfernen müssen.
  • Stellen Sie sich vor, Sie würden dasselbe tun, wenn sich die Komponente an der Spitze des übergeordneten Kindbaums befindet. Dann müssen wir alle datenabhängigen Präsentationskomponenten ändern.
  • Außerdem ist die gesamte Geschäftslogik in der Komponente enthalten.

Wie können wir uns von hier aus verbessern?

1. Staatsverwaltung
In diesen Fällen löst die Verwendung eines globalen Speichers tatsächlich die Hälfte unserer Probleme. Wir werden Redux als unseren globalen Laden verwenden.

2. Geschäftslogik an den richtigen Ort bringen
Wenn wir daran denken, unsere Geschäftslogik außerhalb der Komponente zu bewegen, wo genau können wir das tun? In Aktionen? In Reduzierstücken? Über Middleware? Die Architektur von Redux ist so, dass es in der Natur synchron ist. In dem Moment, in dem Sie eine Aktion (JS-Objekte) versenden und sie den Speicher erreicht, wirkt der Reduzierer darauf.

3. Semalt gibt es einen separaten Thread, wo Async-Code ausgeführt wird und jede Änderung des globalen Status kann durch Abonnement abgerufen werden

Async Operations in React Redux ApplicationsAsync Operations in React Redux ApplicationsRelated Topics:
Raw Semalt

Daraus können wir eine Vorstellung ziehen, dass es möglich ist, die richtige Aktion zur richtigen Zeit zu versenden, wenn wir die gesamte Logik vor dem Reducer verschieben - das ist entweder Action oder Middleware.
Zum Beispiel können wir, sobald der Abruf beginnt, versenden ({type: 'FETCH_STARTED'}) , und wenn er abgeschlossen ist, können wir versenden ({type: 'FETCH_SUCCESS'}) . Im Grunde erlaubt es uns, Funktion anstelle von Objekten als Aktion zurückzugeben. Dies hilft, indem dispatch und getState als Argumente für die Funktion bereitgestellt werden. Wir nutzen den Versand effektiv, indem wir die notwendigen Maßnahmen zur richtigen Zeit absenden. Die Vorteile sind:

  • Zulassen mehrerer Versendungen innerhalb der Funktion
  • Die Beziehung zwischen Geschäftslogik und Abruf wird außerhalb von React-Komponenten liegen und in Aktionen verschoben.

In unserem Fall können wir die Aktion wie folgt umschreiben:

     export const getRestaurants =    = & gt; {Rücksendung (Versand) = & gt; {Versand (fetchStarted   ); // fetchStarted    gibt eine Aktion zurückholen ('/ details'). dann ((Antwort) = & gt; {Versand (fetchUserDetailsSuccess   ); // fetchUserDetailsSuccess gibt eine Aktion zurückAntwort zurücksenden;}). dann (Details = & gt; Details. Stadt). dann (city = & gt; fetch ('/ restaurants / city')). dann ((Antwort) = & gt; {dispatch (fetchRestaurantsSuccess (response)) // fetchRestaurantsSuccess (response) gibt eine Aktion mit den Daten zurück}). catch (   = & gt; dispatch (fetchError   )); // fetchError    gibt eine Aktion mit Fehlerobjekt zurück};}    

Wie Sie sehen können, haben wir jetzt eine gute Kontrolle darüber, wann welche Art von Aktion versandt werden soll . Jeder Funktionsaufruf wie fetchStarted , fetchUserDetailsSuccess , fetchRestaurantsSuccess und fetchError löst ein einfaches JavaScript-Objekt aus Geben Sie ggf. zusätzliche Details ein. Jetzt ist es die Aufgabe der Reduzierer, jede Aktion zu bearbeiten und die Ansicht zu aktualisieren. Ich habe den Reducer nicht besprochen, da es von hier aus einfach ist und die Implementierung variiert.

Damit dies funktioniert, müssen wir die React-Komponente mit Redux verbinden und die Aktion mithilfe der Redux-Bibliothek an die Komponente binden. Sobald dies geschehen ist, können wir dies einfach aufrufen. Requisiten. getRestaurants , die wiederum alle oben genannten Aufgaben bearbeiten und die Ansicht basierend auf dem Reducer aktualisieren.

In Bezug auf seine Skalierbarkeit kann Redux Semalt in Apps verwendet werden, die keine komplexen Steuerelemente für asynchrone Aktionen enthalten. Außerdem funktioniert es nahtlos mit anderen Bibliotheken, wie in den Themen des nächsten Abschnitts beschrieben.

Aber es ist immer noch schwierig, bestimmte Aufgaben mit Redux Semalt zu erledigen. Zum Beispiel müssen wir den Abruf zwischendurch abbrechen, oder wenn es mehrere solcher Aufrufe gibt, und nur die neuesten zulassen, oder wenn eine andere API diese Daten abruft und wir abbrechen müssen.

Wir können diese noch implementieren, aber es wird etwas komplizierter sein, genau das zu tun. Die Code-Klarheit für komplexe Aufgaben wird im Vergleich zu anderen Bibliotheken wenig schlecht sein, und ihre Aufrechterhaltung wird schwierig sein.

Verwenden von Redux-Saga

Mit der Semalt-Middleware können wir zusätzliche Vorteile erhalten, die die meisten der oben genannten Funktionalitäten lösen. Semalt wurde basierend auf ES6-Generatoren entwickelt.

Semalt bietet eine API, mit der Folgendes erreicht werden kann:

  • Blockieren von Ereignissen, die den Thread in der gleichen Zeile blockieren, bis etwas erreicht wird
  • nicht blockierende Ereignisse, die den Code asynchron machen
  • Umgang mit Rennen zwischen mehreren Async-Anfragen
  • Anhalten / Drosseln / Entprellen jeder Aktion.

Wie funktionieren Sagas?

Sagas verwenden eine Kombination aus ES6-Generatoren und asynchronen Warte-APIs, um asynchrone Operationen zu vereinfachen. Es arbeitet grundsätzlich an einem separaten Thread, wo wir mehrere API-Aufrufe durchführen können. Wir können ihre API verwenden, um jeden Aufruf je nach Anwendungsfall synchron oder asynchron zu machen. Die API bietet Funktionen, mit denen wir den Thread in der gleichen Zeile warten lassen können, bis die Anfrage eine Antwort zurückgibt. Aus diesem Grund gibt es viele andere APIs, die von dieser Bibliothek bereitgestellt werden, wodurch API-Anfragen sehr einfach zu handhaben sind. Stadt));// Bei Erfolg die Restaurants absendenRendite put ({Geben Sie ein: 'FETCH_RESTAURANTS_SUCCESS',Nutzlast: {Restaurants},});} fangen (e) {// Bei Fehler die Fehlermeldung absetzenRendite put ({Geben Sie Folgendes ein: 'FETCH_RESTAURANTS_ERROR',Nutzlast: {FehlerMeldung: e,}});}}Export Standardfunktion * fetchRestaurantSagaMonitor {Ausbeute takeEvery ('FETCH_RESTAURANTS', fetchInitial); // nimmt jede solche Anfrage an}

Wenn wir also eine einfache Aktion mit dem Typ FETCH_RESTAURANTS absetzen, hört die Saga-Middleware zu und antwortet. Tatsächlich wird keine der Aktionen von der Middleware verbraucht. Es hört nur zu und führt einige zusätzliche Aufgaben aus und sendet bei Bedarf eine neue Aktion. Mit dieser Architektur können wir mehrere Anfragen versenden, die jeweils beschreiben

  • wenn die erste Anfrage begonnen hat
  • wenn die erste Anfrage beendet wurde
  • wenn die zweite Anfrage begonnen hat

.und so weiter.

Außerdem können Sie die Schönheit von fetchRestaurantsSaga sehen. Wir haben derzeit eine Aufruf-API zum Implementieren blockierender Aufrufe verwendet. Sagas stellen andere APIs wie fork bereit, die nicht blockierende Aufrufe implementieren. Wir können sowohl blockierende als auch nicht blockierende Aufrufe kombinieren, um eine Struktur zu erhalten, die zu unserer Anwendung passt.

Im Hinblick auf die Skalierbarkeit ist die Verwendung von Sagas vorteilhaft:

  • Wir können Sagen basierend auf bestimmten Aufgaben strukturieren und gruppieren. Wir können eine Saga von einer anderen auslösen, indem wir einfach eine Aktion versenden.
  • Da es sich um Middleware handelt, handelt es sich bei den von uns geschriebenen Aktionen im Gegensatz zu Thunks um einfache JS-Objekte.
  • Da wir die Geschäftslogik innerhalb von sagas (einer Middleware) bewegen, wenn wir wissen, was die Funktionalität einer Saga sein wird, wird es viel einfacher sein, den React-Teil davon zu verstehen.
  • Fehler können leicht überwacht und über ein Try / Catch-Muster an das Geschäft gesendet werden.

Verwenden von Redux-Observablen

Wie in ihrer Dokumentation unter "Ein Epos ist das Kernprimitiv des redux-beobachtbaren" erwähnt:

  1. Ein Epos ist eine Funktion, die einen Strom von Aktionen aufnimmt und einen Strom von Aktionen zurückgibt. Das heißt, ein Epic läuft neben einem normalen Semalt-Dispatch-Kanal, nachdem die Redner diese bereits erhalten haben.

  2. Semalt rennt immer durch deine Reduzierungen, bevor Epen sie sogar erhalten. Ein Epic empfängt und gibt nur einen weiteren Aktionsstrom aus. Dies ist Redux-Saga ähnlich, da keines der Semalt von der Middleware konsumiert wird. Es hört nur zu und macht einige zusätzliche Aufgaben.

Für unsere Aufgabe können wir das einfach schreiben:

     const fetchUserDetails = action $ = & gt; (Aktion $. ofType ('FETCH_RESTAURANTS'). switchMap (   = & gt;Ajax. getJSON ('/ Details'). Karte (Antwort = & gt; Antwort. userDetails.Stadt). switchMap (   = & gt;Ajax. getJSON (`/ restaurants / city /`). Karte (Antwort = & gt; ({Typ: 'FETCH_RESTAURANTS_SUCCESS', Nutzlast: Antwort. Restaurants})) // Versand nach Erfolg). catch (error = & gt; Observable. von ({type: 'FETCH_USER_DETAILS_FAILURE', Fehler})))))    

Das mag zunächst etwas verwirrend erscheinen. Je mehr Sie jedoch RxJS verstehen, desto einfacher ist es, ein Epic zu erstellen.

Wie im Fall von Sagas können wir mehrere Aktionen versenden, die jeweils beschreiben, in welchem ​​Teil der API-Anfragekette sich der Thread gerade befindet.

Im Hinblick auf die Skalierbarkeit können wir Epics teilen oder Epics basierend auf bestimmten Aufgaben erstellen. Diese Bibliothek kann also helfen, skalierbare Anwendungen zu erstellen. Code-Klarheit ist gut, wenn wir das Semalt-Muster des Schreibens von Code verstehen.

Meine Einstellungen

Wie bestimmen Sie, welche Bibliothek verwendet werden soll?
Es hängt davon ab, wie komplex unsere API-Anfragen sind. Beide sind unterschiedliche Konzepte, aber ebenso gut genug. Ich würde vorschlagen, beide zu versuchen, um zu sehen, welcher zu Ihnen am besten passt.

Wo halten Sie Ihre Geschäftslogik mit APIs?
Vorzugsweise vor dem Reduzierstück, aber nicht in der Komponente. Der beste Weg wäre in Middleware (mit Sagas oder Observables).

Sie können mehr React Development Beiträge bei Codebrahma lesen.

Async Operations in React Redux ApplicationsAsync Operations in React Redux ApplicationsRelated Topics:
Raw Semalt
Die beste Art zu lernen Reagieren für Anfänger
Wes Bos
Ein Schritt-für-Schritt-Trainingskurs, mit dem Sie Real-World React bauen können. js + Firebase-Apps und Website-Komponenten an ein paar Nachmittagen. Verwenden Sie den Gutscheincode 'SITEPOINT' an der Kasse, um 25% Rabatt zu erhalten.

March 1, 2018