Les bases de la programmation réactive à l'aide de RxJS

Partie 1. Réactivité et flux


Cette série d'articles se concentre sur la réactivité et son application dans JS en utilisant une bibliothèque aussi merveilleuse que RxJS.

Série d'articles "Fondamentaux de la programmation réactive utilisant RxJS":



À qui s'adresse cet article : en gros, ici je vais expliquer les bases, donc l'article est principalement destiné aux débutants dans cette technologie. En même temps, j'espère que les développeurs expérimentés pourront apprendre quelque chose de nouveau par eux-mêmes. La compréhension nécessitera la connaissance de js (es5 / es6).

Motivation : J'ai rencontré RxJS pour la première fois quand j'ai commencé à travailler avec angular. C'est alors que j'ai eu du mal à comprendre le mécanisme de réactivité. Des difficultés ont été ajoutées par le fait qu'au moment du début de mon travail la plupart des articles étaient consacrés à l'ancienne version de la bibliothèque. J'ai dû lire beaucoup de documentation, divers manuels pour au moins comprendre quelque chose. Et ce n'est qu'après un certain temps que j'ai commencé à réaliser comment «tout est arrangé». Pour faciliter la vie des autres, j'ai décidé de tout mettre sur les étagères.

Qu'est-ce que la réactivité?


Il est difficile de trouver une réponse à un terme apparemment courant. En bref: la réactivité est la capacité de répondre à tout changement. Mais de quels changements parle-t-on? Tout d'abord, sur les changements de données. Prenons un exemple:

let a = 2; let b = 3; let sum = a + b; console.log(sum); // 5 a = 3; console.log(sum); // 5 -    

Cet exemple illustre le paradigme de programmation impératif familier. Contrairement à l'approche impérative, l'approche réactive s'appuie sur des stratégies de propagation de changement poussé. La stratégie de transmission implique qu'en cas de modifications des données, ces mêmes modifications seront «répercutées» et les données qui en dépendent seront automatiquement mises à jour. Voici comment notre exemple se comporterait si une stratégie push était appliquée:

 let a = 2; let b = 3; let sum = a + b; console.log(sum); // 5 a = 3; console.log(sum); // 6 -   sum   

Cet exemple montre une approche réactive. Il est à noter que cet exemple n'a rien à voir avec la réalité, je ne l'ai donné que pour montrer la différence d'approche. Le code réactif dans les applications du monde réel sera très différent, et avant de passer à la pratique, nous devrions parler d'un autre élément important de la réactivité.

Flux de données


Si vous regardez le terme «programmation réactive» sur Wikipédia, le site nous donnera la définition suivante: «La programmation réactive est un paradigme de programmation axé sur les flux de données et la propagation des changements.» A partir de cette définition, nous pouvons conclure que la réactivité est basée sur deux «baleines» principales. J'ai mentionné la répartition des changements ci-dessus, nous ne nous attarderons donc pas davantage sur ce point. Mais nous devrions parler davantage des flux de données. Regardons l'exemple suivant:

 const input = document.querySelector('input'); //     const eventsArray = []; input.addEventListener('keyup', event => eventsArray.push(event) ); //      eventsArray 

Nous écoutons l'événement keyup et mettons l'objet événement dans notre tableau. Au fil du temps, notre tableau peut contenir des milliers d'objets KeyboardEvent. Il convient de noter que notre tableau est trié par heure - l'indice des événements ultérieurs est supérieur à celui des événements précédents. Un tel tableau est un modèle de flux de données simplifié. Pourquoi simplifié? Parce que le tableau ne peut stocker que des données. Nous pouvons également parcourir le tableau et en quelque sorte traiter ses éléments. Mais le tableau ne peut pas nous dire qu'un nouvel élément lui a été ajouté. Afin de savoir si de nouvelles données ont été ajoutées au tableau, nous devons à nouveau les itérer.

Mais que se passe-t-il si notre réseau sait nous informer que de nouvelles données y sont arrivées? Un tel tableau pourrait certainement être appelé un flux. Nous arrivons donc à la définition du flux. Un flux est un tableau de données triées par heure qui peut indiquer que les données ont changé.

Observable


Maintenant que nous savons ce que sont les flux, travaillons avec eux. Dans RxJS, les flux sont représentés par la classe Observable. Pour créer votre propre flux, il suffit d'appeler le constructeur de cette classe et de lui passer la fonction d'abonnement comme argument:

 const observable = new Observable(observer => { observer.next(1); observer.next(2); observer.complete(); }) 

En appelant le constructeur de la classe Observable, nous créons un nouveau thread. Comme argument, nous avons passé la fonction d'abonnement au constructeur. La fonction d'abonnement est une fonction régulière qui prend un observateur comme paramètre. L'observateur lui-même est un objet qui a trois méthodes:

  • suivant - jette une nouvelle valeur dans le flux
  • erreur - jette une erreur dans le flux, après quoi le flux se termine
  • complete - termine le fil

Nous avons donc créé un thread qui émet deux valeurs et se termine.

Abonnement


Si nous exécutons le code précédent, rien ne se passera. Nous allons uniquement créer un nouveau flux et enregistrer le lien vers celui-ci dans la variable observable, mais le flux lui-même n'émettra jamais une seule valeur. En effet, les threads sont des objets «paresseux» et ne font rien par eux-mêmes. Pour que notre flux commence à émettre des valeurs et que nous puissions traiter ces valeurs, nous devons commencer à «écouter» le flux. Vous pouvez le faire en appelant la méthode subscribe sur l'objet observable.

 const observer = { next: value => console.log(value), // 1, 2 error: error => console.error(error), // complete: () => console.log("completed") // completed }; observable.subscribe(observer); 

Nous avons identifié notre observateur et lui avons décrit trois méthodes: ensuite, erreur, complète. Les méthodes consignent simplement les données transmises en tant que paramètres. Ensuite, nous appelons la méthode subscribe et lui passons notre observateur. Au moment d'appeler subscribe, la fonction d'abonnement est appelée, celle que nous avons passée au constructeur au stade de la déclaration de notre flux. Ensuite, le code de la fonction d'abonnement sera exécuté, qui transmet deux valeurs à notre observateur, puis termine le flux.

Certes, beaucoup ont une question, que se passera-t-il si nous nous abonnons à nouveau au flux? Tout sera pareil: le flux transmettra à nouveau deux valeurs à l'observateur et se terminera. Chaque fois que la méthode d'abonnement est appelée, une fonction d'abonnement sera appelée et tout son code sera à nouveau exécuté. De cela, nous pouvons conclure: peu importe combien de fois nous nous abonnons au flux, nos observateurs recevront les mêmes données.

Se désinscrire


Essayons maintenant d'implémenter un exemple plus complexe. Nous allons écrire un minuteur qui comptera quelques secondes à partir du moment de la souscription et les transmettrons aux observateurs.

 const timer = new Observable(observer => { let counter = 0; //  setInterval(() => { observer.next(counter++); //          }, 1000); }); timer.subscribe({ next: console.log //    }); 

Le code est assez simple. Dans la fonction d'abonnement, nous déclarons une variable de compteur. Ensuite, en utilisant la fermeture, nous accédons à la variable à partir de la fonction flèche dans setInterval. Et chaque seconde, nous passons la variable à l'observateur, après quoi nous l'incrémentons. Ensuite, abonnez-vous au flux, spécifiez une seule méthode - suivante. Ne vous inquiétez pas, nous n'avons pas annoncé d'autres méthodes. Aucune des méthodes d'observation n'est requise. On peut même passer un objet vide, mais dans ce cas le fil sera gâché.

Après le lancement, nous verrons les journaux convoités qui apparaîtront chaque seconde. Si vous le souhaitez, vous pouvez expérimenter et vous abonner au flux plusieurs fois. Vous verrez que chacun des threads sera exécuté indépendamment des autres.

Si vous y réfléchissez, notre thread sera exécuté pendant toute la durée de vie de l'application, car nous n'avons aucune logique pour annuler setInterval, et dans la fonction d'abonnement, il n'y a pas d'appel à la méthode complète. Mais que se passe-t-il si nous avons besoin du fil pour terminer?

En fait, tout est très simple. Si vous regardez la documentation, vous pouvez voir que la méthode subscribe renvoie un objet d'abonnement. Cet objet a une méthode de désabonnement. Nous l'appelons, et notre observateur cessera de recevoir des valeurs du flux.

 const subscription = timer.subscribe({next: console.log}); setTimeout(() => subscription.unsubscribe(), 5000); //   5  

Après le démarrage, nous verrons que le compteur s'arrête à 4. Mais, bien que nous nous soyons désabonnés du flux, notre fonction setInterval continue de fonctionner. Elle incrémente notre compteur à chaque seconde et le passe à l'observateur factice. Pour éviter cela, vous devez écrire la logique d'annulation de l'intervalle. Pour ce faire, vous devez renvoyer une nouvelle fonction à partir de la fonction d'abonnement dans laquelle la logique d'annulation sera implémentée.

 const timer = new Observable(observer => { let counter = 0; const intervalId = setInterval(() => { observer.next(counter++); }, 1000); return () => { clearInterval(intervalId); } }); 

Nous pouvons maintenant pousser un soupir de soulagement. Après avoir appelé la méthode unsubscribe, notre fonction unsubscribe sera appelée, ce qui effacera l'intervalle.

Conclusion


Cet article montre les différences entre les approches impératives et réactives, ainsi que des exemples de création de vos propres flux. Dans la partie suivante, je parlerai des autres méthodes de création de threads et de la façon de les appliquer.

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


All Articles