Points clés
Cet article a été initialement publié dans CodeBrahma.
JavaScript est un langage de programmation unique. C'est-à-dire que lorsque vous écrivez le code suivant ...
… La deuxième ligne ne sera exécutée qu'après l'exécution de la première ligne. Ce ne sera pas un problème dans la plupart des cas, car le client ou le serveur effectue des millions de calculs par seconde. Nous ne remarquons ces effets que lorsque nous effectuons des calculs coûteux (une tâche qui prend un certain temps à compléter - une demande de réseau prend un certain temps à revenir).
Pourquoi est-ce que je montre uniquement des appels API (demandes de réseau) ici? Qu'en est-il des autres opérations asynchrones? Les appels API sont un exemple très simple et utile pour décrire comment gérer les opérations asynchrones. Il existe d'autres opérations, telles que setTimeout()
, l'informatique à forte intensité de performances, le chargement d'image et toutes les opérations axées sur les événements.
Lors de la création d'une application, nous devons considérer comment l'exécution asynchrone affecte la structure. Par exemple, pensez à fetch()
comme une fonction qui effectue un appel API (demande de réseau) du navigateur. (Ignorez s'il s'agit d'une demande Ajax. Traitez simplement son comportement comme asynchrone ou synchrone.) Le temps écoulé lorsque la demande est traitée sur le serveur ne se produit pas sur le thread principal. Par conséquent, votre code JS continuera d'exécuter et une fois que la demande renvoie une réponse, elle mettra à jour le thread.
Considérez ce code:
userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
Dans ce cas, puisque fetch()
est asynchrone, lorsque nous essayons d'obtenir userDetails
, nous n'aurons pas userId
. Nous devons donc le construire d'une manière qui garantit que la deuxième ligne n'est exécutée qu'après que la première ligne renvoie la réponse.
La plupart des implémentations de demande de réseau modernes sont asynchrones. Mais cela ne fonctionne pas toujours parce que nous comptons sur les données de réponse API précédentes pour les appels API ultérieurs. Voyons comment le construire spécifiquement dans une application ReactJS / Redux.
React est une bibliothèque frontale pour créer des interfaces utilisateur. Redux est un conteneur d'état qui gère l'intégralité de l'état d'une application. L'utilisation de React with Redux nous permet de créer des applications efficaces et évolutives. Dans une telle application React, il existe plusieurs façons de construire des opérations asynchrones. Pour chaque méthode, nous discuterons de ses avantages et inconvénients en termes des facteurs suivants:
Pour chaque méthode, nous exécuterons ces deux appels API:
userDetails
Supposons que le point final soit . Il comprendra la ville dans la réponse. La réponse sera un objet:
2.
/details
Supposons que le point final soit
userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
/restuarants/:city
en utilisant redux thunk
userDetails: { … city: 'city', … };
en utilisant des observables redux
setState
J'ai spécifiquement choisi les méthodes ci-dessus car ce sont les méthodes les plus couramment utilisées dans les grands projets. Il existe encore d'autres méthodes qui peuvent être plus spécifiques à une tâche spécifique et n'ont pas toutes les fonctionnalités requises par une application complexe (par exemple Dans notre exemple, nous utiliserons la bibliothèque Axios pour obtenir les données, ce qui renvoie une promesse lorsque nous ferons une demande de réseau. La promesse peut analyser et renvoyer une réponse ou lancer une erreur. Ainsi, une fois que le composant réact
est monté, nous pouvons l'obtenir directement comme ceci:De cette manière, lorsque l'État change (en raison de l'acquisition), le composantsera automatiquement renforcer et charger la liste des restaurants.
Async / Await est une nouvelle implémentation que nous pouvons utiliser pour effectuer des opérations asynchrones. Par exemple, la même fonction peut être obtenue par:
Les deux méthodes sont les plus faciles. Étant donné que toute la logique est à l'intérieur du composant, nous pouvons facilement obtenir toutes les données à la fois après le chargement du composant.['restaurant1', 'restaurant2', …]
Inconvénients de cette méthode Le problème se pose lorsque des interactions complexes basées sur des données se produisent. Par exemple, considérez la situation suivante:
La gestion du statut utilisant le stockage global peut réellement résoudre la moitié de nos problèmes dans ces cas. Nous utiliserons Redux comme stockage global.
Déplacez la logique métier au bon endroit si nous envisageons de déplacer la logique métier hors du composant, où pouvons-nous exactement le faire? Dans les actions? Dans les réducteurs? Via middleware? L'architecture de Redux est synchrone. Une fois que vous avez distribué une action (objet JS) et qu'il atteint le stockage, le réducteur fonctionnera dessus.
Assurez-vous qu'il existe un thread séparé qui exécute le code asynchrone et que toute modification de l'état global peut être récupérée par abonnement
À partir de cela, nous pouvons apprendre que si nous déplaçons toute la logique d'acquisition avant le réducteur - c'est-à-dire l'action ou le middleware - alors nous pouvons distribuer l'action correcte au bon moment. Par exemple, une fois que la récupération commence, nous pouvons distribuer dispatch({ type: 'FETCH_STARTED' })
, et lorsqu'elle est terminée, nous pouvons distribuer dispatch({ type: 'FETCH_SUCCESS' })
.
Vous souhaitez développer une application React JS?
redux Thunk est un middleware pour redux. Il nous permet essentiellement de renvoyer des fonctions au lieu d'objets comme action. Cela aide à fournir dispatch
et getState
comme paramètres de la fonction. Nous utilisons dispatch
pour distribuer les actions nécessaires au bon moment. Les avantages sont:
Dans notre exemple, nous pouvons réécrire l'action comme ceci:
userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
Comme vous pouvez le voir, nous avons maintenant un bon contrôle sur le type d'action distribué. Chaque appel de fonction (comme fetchStarted()
, fetchUserDetailsSuccess()
, fetchRestaurantsSuccess()
et fetchError()
) distribuera une action de l'objet JavaScript normal de type, et des détails supplémentaires peuvent être ajoutés si nécessaire. Alors maintenant, la tâche de réducteur est de traiter chaque action et de mettre à jour la vue. Je n'ai pas discuté de réducteur car c'est simple d'ici et la mise en œuvre peut être différente.
Pour que cela fonctionne, nous devons connecter le composant React à Redux et lier l'action au composant à l'aide de la bibliothèque Redux. Une fois terminé, nous pouvons simplement appeler this.props.getRestaurants()
, qui à son tour gérera toutes les tâches ci-dessus et mettra à jour la vue en fonction du réducteur.
Pour son évolutivité, Redux Thunk peut être utilisé pour des applications qui n'impliquent pas de contrôle complexe des actions asynchrones. De plus, il fonctionne de manière transparente avec d'autres bibliothèques, comme décrit dans le sujet dans la section suivante.
Cependant, il est encore un peu difficile d'effectuer certaines tâches à l'aide de Redux Thunk. Par exemple, nous devons mettre en pause l'opération de récupération intermédiaire, ou autoriser uniquement les derniers appels lorsqu'il y a plusieurs appels de tels, ou si d'autres API obtiennent ces données et que nous devons annuler.
Nous pouvons toujours les implémenter, mais il sera plus compliqué à exécuter avec précision. Par rapport aux autres bibliothèques, la clarté du code des tâches complexes sera légèrement pire et sera plus difficile à entretenir.
Avec le middleware Redux-Saga, nous pouvons obtenir l'avantage supplémentaire pour aborder la plupart des fonctionnalités ci-dessus. Redux-Saga est développé sur la base du générateur ES6.
redux-saga fournit une API qui aide à atteindre les objectifs suivants:
Saga utilise une combinaison de générateur ES6 et d'API asynchronisée / attendre pour simplifier les opérations asynchrones. Il fait essentiellement son travail sur ses fils séparés où nous pouvons passer plusieurs appels d'API. Nous pouvons utiliser leur API pour rendre chaque appel synchrone ou asynchrone, selon le cas d'utilisation. L'API offre la possibilité de faire attendre un fil sur la même ligne jusqu'à ce que la demande renvoie une réponse. En dehors de cela, cette bibliothèque fournit de nombreuses autres API, qui rendent les demandes d'API très faciles à gérer.
Considérons notre exemple précédent: si nous initialisons une saga et la configurons avec Redux en fonction de ce qui est mentionné dans sa documentation, nous pouvons effectuer ce qui suit:
userDetails: { … city: 'city', … };
Donc, si nous le distribuons en utilisant une action simple de type FETCH_RESTAURANTS
, le middleware Saga écoutera et répondra. En fait, aucune action n'est utilisée par le middleware. Il écoute et effectue simplement d'autres tâches, et distribue de nouvelles actions si nécessaire. En utilisant cette architecture, nous pouvons distribuer plusieurs demandes, chacune décrivant:
et ainsi de suite.
De plus, vous pouvez voir les avantages de fetchRestaurantSaga()
. Nous utilisons actuellement l'API call
pour implémenter les appels de blocage. Saga fournit d'autres API, telles que fork()
, qui implémente les appels non bloquants. Nous pouvons combiner des appels de blocage et non bloquant pour maintenir une structure adaptée à notre application.
avec évolutivité, l'utilisation de la saga est bénéfique:
Comme indiqué dans sa documentation, "Epic est la primitive principale de la section" obstatoire ":
redux-observable"Pour notre tâche, nous pouvons simplement écrire le code suivant:
userId = fetch(userEndPoint); // 从 userEndpoint 获取 userId userDetails = fetch(userEndpoint, userId) // 为此特定 userId 获取数据。
Au début, cela peut sembler un peu déroutant. Cependant, plus vous comprenez RXJS, plus il est facile de créer une épopée.
Comme la saga, nous pouvons distribuer plusieurs actions, chacune décrivant dans quelle partie de la chaîne de demande d'API dans laquelle se trouve le thread.
En termes d'évolutivité, nous pouvons diviser ou combiner Epic en fonction d'une tâche spécifique. Par conséquent, cette bibliothèque peut aider à créer des applications évolutives. Si nous comprenons le modèle observable d'écriture de code, la clarté du code est bonne.
Comment déterminer la bibliothèque à utiliser? Cela dépend de la complexité de nos demandes d'API.
Comment choisir entre redux-saga et redux-observable? Cela dépend du générateur d'apprentissage ou RXJS. Les deux sont des concepts différents, mais tout aussi bons. Je suggère d'essayer les deux de voir lequel est le meilleur pour vous.
Où met la logique métier pour le traitement des API? Il est préférable de le mettre avant le réducteur, mais pas dans la composante. La meilleure façon est dans le middleware (en utilisant la saga ou observable).
Vous pouvez lire plus d'articles de développement React à CodeBrahma.
Le middleware in redux joue un rôle crucial dans la gestion des opérations asynchrones. Il fournit un point d'extension tiers entre l'action de distribution et l'action arrive au réducteur. Le middleware peut être utilisé pour enregistrer, modifier et même annuler des actions, ainsi que pour distribuer d'autres actions. Dans le contexte des opérations asynchrones, le middleware comme Redux Thunk ou Redux vous permet d'écrire des créateurs d'action qui renvoient les fonctions au lieu de l'action. Cette fonction peut ensuite être utilisée pour retarder la distribution d'une action ou pour ne distribuer une action que lorsqu'une condition spécifique est remplie.
Redux Thunk est un middleware qui vous permet d'écrire des créateurs d'action qui renvoient les fonctions au lieu de l'action. Thunk peut être utilisé pour retarder la distribution des actions ou pour distribuer des actions uniquement lorsque certaines conditions sont remplies. Cette fonctionnalité en fait un excellent outil pour gérer les opérations asynchrones dans Redux. Par exemple, vous pouvez distribuer une action pour indiquer le début d'un appel API, puis distribuer une autre action lorsque l'appel renvoie les données ou le message d'erreur.
Redux Thunk et Redux Saga sont tous deux du middleware pour gérer les effets secondaires dans Redux, y compris les opérations asynchrones. La principale différence entre les deux est leur approche. Redux Thunk utilise des fonctions de rappel pour gérer les opérations asynchrones, tandis que Redux Saga utilise des fonctions de générateur et des méthodes plus déclaratives. Cela rend Redux Saga plus puissant et plus flexible, mais aussi plus complexe. Si votre application a des opérations asynchrones simples, Redux Thunk peut suffire. Cependant, pour des scénarios plus complexes impliquant des conditions de course, une annulation et une logique IF-Else, la saga Redux peut être un meilleur choix.
L'action peut être distribuée lorsqu'une erreur se produit pendant une opération asynchrone pour gérer la gestion des erreurs en fonctionnement asynchrone dans Redux. Cette action peut prendre son message d'erreur comme charge utile. Vous pouvez ensuite gérer cette action dans le réducteur pour mettre à jour l'état avec le message d'erreur. De cette façon, un message d'erreur peut être affiché à l'utilisateur ou enregistré pour débogage.
Les actions asynchrones dans Redux peuvent être testées en moquetant Redux Storage et API. Pour le stockage Redux, vous pouvez utiliser des bibliothèques comme redux-mock-store
. Pour les appels API, vous pouvez utiliser des bibliothèques comme fetch-mock
ou nock
. Dans vos tests, vous pouvez distribuer une action asynchrone, puis affirmer que l'action attendue a été distribuée avec la charge utile correcte.
Le middleware comme Redux Saga peut être utilisé pour annuler les opérations asynchrones dans Redux. Redux Saga utilise des fonctions de générateur, qui peuvent être annulées à l'aide de l'effet cancel
. Lorsque l'effet cancel
est donné, la saga sera annulée de son point de démarrage jusqu'à ce que l'effet actuel soit annulé.
Le middleware comme Redux Saga peut être utilisé pour gérer les conditions de course dans les opérations asynchrones dans Redux. Redux Saga fournit des effets comme takeLatest
et takeEvery
qui peuvent être utilisés pour gérer les actions simultanées. Par exemple, si la tâche SAGA précédemment démarrée est toujours en cours d'exécution lorsqu'une nouvelle action est distribuée, takeLatest
annule la tâche.
redux Thunk supporte nativement l'async / attendre. Dans votre créateur d'action, vous pouvez retourner des fonctions asynchrones au lieu de fonctions régulières. À l'intérieur de cette fonction asynchrone, vous pouvez utiliser Async / Await pour gérer les opérations asynchrones. Lorsque l'opération asynchrone est terminée, la fonction dispatch
peut être appelée à l'aide de l'objet Action.
L'état de chargement dans les opérations asynchrones dans Redux peut être géré en distribuant des actions avant et après les opérations asynchrones. L'action distribuée avant l'opération peut définir l'état de charge sur true, et l'action distribuée après l'opération peut la définir sur False. Dans votre réducteur, vous pouvez gérer ces actions pour mettre à jour l'état de charge dans le stockage.
Les effets secondaires de Redux peuvent être manipulés à l'aide de middleware comme Redux Thunk ou Redux Saga. Ces middleware vous permet d'écrire des créateurs d'action qui renvoient les fonctions au lieu de l'action. Cette fonction peut être utilisée pour effectuer des effets secondaires tels que les opérations asynchrones, l'exploitation forestière et la distribution conditionnellement.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!