Comment utiliser les événements dans Node.js de la bonne manière

Avant que la programmation événementielle ne devienne populaire, la manière standard de communiquer entre les différentes parties d'une application était assez simple: un composant qui voulait envoyer un message à un autre invoquait explicitement une méthode sur ce composant. Mais le code événementiel est écrit pour réagir plutôt que pour être appelé .

Les avantages du concours complet

Cette approche fait que nos composants sont beaucoup plus découplés . Au fur et à mesure que nous continuons à écrire une application, nous identifions les événements en cours de route. Nous les renvoyons au bon moment et attachons un ou plusieurs écouteurs d'événements à chacun. L'extension des fonctionnalités devient beaucoup plus facile. Nous pouvons ajouter plus d'auditeurs à un événement particulier. Nous ne modifions pas les écouteurs existants ou la partie de l'application à partir de laquelle l'événement a été déclenché. Ce dont nous parlons est le modèle Observer.

Concevoir une architecture événementielle

Identifier les événements est assez important. Nous ne voulons pas finir par devoir supprimer / remplacer des événements existants du système. Cela pourrait nous forcer à supprimer / modifier un nombre quelconque d'écouteurs attachés à l'événement. Le principe général que j'utilise est d' envisager de déclencher un événement uniquement lorsqu'une unité de logique métier termine l'exécution.

Supposons que vous souhaitiez envoyer un tas d'e-mails différents après l'enregistrement d'un utilisateur. Désormais, le processus d'enregistrement lui-même peut impliquer de nombreuses étapes et requêtes complexes. Mais d'un point de vue commercial, c'est une étape. Et chacun des e-mails à envoyer sont également des étapes individuelles. Il serait donc logique de déclencher un événement dès que l'inscription est terminée. Nous avons plusieurs auditeurs qui y sont attachés, chacun étant responsable de l'envoi d'un type d'e-mail.

L'architecture asynchrone basée sur les événements de Node comporte certains types d'objets appelés «émetteurs». Ils émettent des événements nommés qui provoquent l'appel de fonctions appelées «écouteurs». Tous les objets qui émettent des événements sont des instances de la classe EventEmitter. En l'utilisant, nous pouvons créer nos propres événements:

Un exemple

Utilisons le module d'événements intégré (que je vous encourage à consulter en détail) pour y accéder EventEmitter.

C'est la partie de l'application où notre serveur reçoit une requête HTTP, enregistre un nouvel utilisateur et émet un événement:

Et un module séparé où nous attachons un auditeur:

C'est une bonne pratique de séparer la politique de la mise en œuvre. Dans ce cas, la politique signifie quels auditeurs sont abonnés à quels événements. La mise en œuvre signifie les auditeurs eux-mêmes.

Cette séparation permet à l'auditeur de devenir également réutilisable. Il peut être attaché à d'autres événements qui envoient le même message (un objet utilisateur). Il est également important de mentionner que lorsque plusieurs écouteurs sont associés à un seul événement, ils seront exécutés de manière synchrone et dans l'ordre dans lequel ils ont été attachés . Par conséquent, someOtherListeneril s'exécutera après la sendEmailOnRegistrationfin de l'exécution.

Cependant, si vous voulez que vos écouteurs s'exécutent de manière asynchrone, vous pouvez simplement envelopper leurs implémentations avec setImmediatecomme ceci:

Gardez vos auditeurs propres

Tenez-vous en au principe de responsabilité unique lors de l'écriture des auditeurs. Un auditeur ne devrait faire qu'une seule chose et bien la faire. Évitez, par exemple, d'écrire trop de conditionnelles dans un écouteur qui décident quoi faire en fonction des données (message) qui ont été transmises par l'événement. Il serait beaucoup plus approprié d'utiliser différents événements dans ce cas:

Détachement explicite des auditeurs lorsque cela est nécessaire

Dans l'exemple précédent, nos auditeurs étaient des fonctions totalement indépendantes. Mais dans les cas où un écouteur est associé à un objet (c'est une méthode), il doit être détaché manuellement des événements auxquels il s'était abonné. Sinon, l'objet ne sera jamais récupéré car une partie de l'objet (l'écouteur) continuera à être référencée par un objet externe (l'émetteur). Ainsi la possibilité d'une fuite de mémoire.

Par exemple, si nous construisons une application de chat et que nous voulons que la responsabilité d'afficher une notification lorsqu'un nouveau message arrive dans une salle de chat à laquelle un utilisateur s'est connecté doit se trouver dans cet objet utilisateur lui-même, nous pouvons faire ceci:

Lorsque l'utilisateur ferme son onglet ou perd sa connexion Internet pendant un certain temps, naturellement, nous pourrions vouloir déclencher un rappel côté serveur qui notifie aux autres utilisateurs que l'un d'eux vient de se déconnecter. À ce stade, bien sûr, cela n'a aucun sens displayNewMessageNotificationd'être invoqué pour l'utilisateur hors ligne. Il continuera à être appelé sur les nouveaux messages à moins que nous ne le supprimions explicitement. Sinon, en dehors de l'appel inutile, l'objet utilisateur restera également en mémoire indéfiniment. Assurez-vous donc d'appeler disconnectFromChatroomvotre rappel côté serveur qui s'exécute chaque fois qu'un utilisateur se déconnecte.

Il faut se méfier

Le couplage lâche dans les architectures événementielles peut également conduire à une complexité accrue si nous ne faisons pas attention. Il peut être difficile de suivre les dépendances dans notre système. Notre application deviendra particulièrement sujette à ce problème si nous commençons à émettre des événements depuis les auditeurs. Cela pourrait éventuellement déclencher des chaînes d'événements inattendus.