Un événement est une notification décrivant une occurrence du passé récent.
Une implémentation typique d'un système piloté par événement utilise un répartiteur d'événement et des fonctions de gestionnaire (ou abonnés ). Le répartiteur fournit une API pour relier les gestionnaires aux événements (jQuery bind
) et une méthode pour publier un événement à ses abonnés ( trigger
dans jQuery). Lorsque vous parlez d'événements IO ou UI, il existe généralement une boucle d'événements , qui détecte les nouveaux événements tels que les clics de souris et les transmet au répartiteur. Dans JS-land, le répartiteur et la boucle d'événement sont fournis par le navigateur.
Pour le code qui interagit directement avec l'utilisateur - répondre aux pressions sur les touches et aux clics - la programmation événementielle (ou une variante de celle-ci, telle que la programmation réactive fonctionnelle ) est presque inévitable. En tant que programmeur, vous ne savez pas quand et où l'utilisateur va cliquer. Vous devez donc utiliser le framework d'interface graphique ou le navigateur pour détecter l'action de l'utilisateur dans sa boucle d'événements et notifier votre code. Ce type d'infrastructure est également utilisé dans les applications réseau (cf. NodeJS).
Votre exemple, dans lequel vous soulevez un événement dans votre code plutôt que d'appeler directement une fonction, présente des compromis plus intéressants, dont je parlerai plus loin. La principale différence est que l'éditeur d'un événement ( makeItSnow
) ne spécifie pas le destinataire de l'appel. c'est câblé ailleurs (dans l'appel à bind
votre exemple). C'est ce qu'on appelle feu et oublie : makeItSnow
annonce au monde qu'il neige, mais peu importe qui écoute, ce qui se passe ensuite ou quand cela se produit, il diffuse simplement le message et se dépoussière.
Ainsi, l'approche événementielle dissocie l'expéditeur du message du destinataire. Un avantage que cela vous offre est qu'un événement donné peut avoir plusieurs gestionnaires. Vous pouvez lier une gritRoads
fonction à votre événement neige sans affecter le shovelSnow
gestionnaire existant . Vous avez la flexibilité dans la manière dont votre application est câblée; pour désactiver un comportement, il vous suffit de supprimer l' bind
appel plutôt que de parcourir le code pour en trouver toutes les instances.
Un autre avantage de la programmation événementielle réside dans le fait qu’elle vous donne un endroit où placer vos préoccupations transversales. Le répartiteur d'événements joue le rôle de médiateur et certaines bibliothèques (telles que Brighter ) utilisent un pipeline afin que vous puissiez facilement brancher des exigences génériques telles que la journalisation ou la qualité de service.
Divulgation complète: Brighter est développé à Huddle, où je travaille.
Un troisième avantage de découplage de l'expéditeur d'un événement du récepteur est qu'il vous donne la flexibilité lorsque vous gérez l'événement. Vous pouvez traiter chaque type d'événement sur son propre thread (si votre répartiteur d'événements le prend en charge) ou vous pouvez placer les événements déclenchés sur un courtier de messages tel que RabbitMQ et les gérer avec un processus asynchrone, voire les traiter en bloc du jour au lendemain. Le destinataire de l'événement peut se trouver dans un processus séparé ou sur une machine séparée. Vous n'avez pas à changer le code qui déclenche l'événement pour le faire! C'est la grande idée derrière les architectures de "microservices": des services autonomes communiquent à l'aide d'événements, avec un middleware de messagerie comme épine dorsale de l'application.
Pour un exemple assez différent de style piloté par les événements, reportez-vous à la conception pilotée par domaine , où les événements de domaine sont utilisés pour aider à garder les agrégats séparés. Par exemple, considérons un magasin en ligne qui recommande des produits en fonction de votre historique d’achat. A Customer
doit avoir son historique d’achats mis à jour lorsqu’un ShoppingCart
paiement est effectué. L' ShoppingCart
agrégat peut notifier le Customer
en soulevant un CheckoutCompleted
événement; le Customer
serait mis à jour dans une transaction séparée en réponse à l'événement.
Le principal inconvénient de ce modèle événementiel est l’indirection. Il est maintenant plus difficile de trouver le code qui gère l'événement car vous ne pouvez pas y accéder simplement à l'aide de votre IDE; vous devez déterminer où l'événement est lié dans la configuration et espérer que vous avez trouvé tous les gestionnaires. Il y a plus de choses à garder dans la tête à tout moment. Les conventions de style de code peuvent être utiles ici (par exemple, placer tous les appels bind
dans un fichier). Pour des raisons de santé mentale, il est important de n'utiliser qu'un seul répartiteur d'événements et de l'utiliser de manière cohérente.
Un autre inconvénient est qu’il est difficile de modifier les événements. Si vous devez modifier le format d'un événement, vous devez également modifier tous les destinataires. Cela est exacerbé lorsque les abonnés d'un événement se trouvent sur différentes machines, car vous devez maintenant synchroniser les versions de logiciels!
Dans certaines circonstances, la performance peut être une préoccupation. Lors du traitement d'un message, le répartiteur doit:
- Recherchez les gestionnaires appropriés dans certaines structures de données.
- Construisez un pipeline de traitement de messages pour chaque gestionnaire. Cela peut impliquer un tas d’allocations de mémoire.
- Appelez dynamiquement les gestionnaires (en utilisant éventuellement la réflexion si la langue le requiert).
Ceci est certainement plus lent qu'un appel de fonction normal, qui consiste uniquement à pousser un nouveau cadre sur la pile. Cependant, la flexibilité offerte par une architecture pilotée par les événements facilite beaucoup l'isolement et l'optimisation du code lent. Avoir la possibilité de soumettre du travail à un processeur asynchrone est une grande victoire ici, car cela vous permet de servir une demande immédiatement alors que le travail acharné est traité en arrière-plan. Quoi qu’il en soit, si vous interagissez avec la base de données ou si vous dessinez à l’écran, les coûts liés aux E / S vont totalement couvrir les coûts de traitement d’un message. Il s'agit d'éviter une optimisation prématurée.
En résumé, les événements sont un excellent moyen de créer des logiciels à couplage lâche, mais ils ne sont pas sans coût. Ce serait une erreur, par exemple, de remplacer chaque appel de fonction dans votre application par un événement. Utilisez les événements pour créer des divisions architecturales significatives.
$(document).bind('snow', shovelShow)
. Pas besoin de l'envelopper dans une fonction anonyme.