Le Cycle d'Événements Node.js
Node.js fonctionne comme une application mono-processus mono-thread, mais grâce aux interfaces de rappel asynchrones fournies par le moteur V8, il peut gérer un grand nombre d'opérations concurrentes, ce qui lui confère une performance élevée.
Presque toutes les API de Node.js prennent en charge les fonctions de rappel.
La quasi-totalité des mécanismes d'événements de Node.js sont implémentés en utilisant le motif de conception observateur.
Le mono-thread de Node.js fonctionne de manière similaire à une boucle while(true) qui ne se termine que lorsqu'il n'y a plus d'observateurs d'événements. Chaque événement asynchrone génère un observateur d'événements, et si un événement se produit, la fonction de rappel correspondante est appelée.
Programmation Dirigée par les Événements
Node.js utilise un modèle de programmation dirigé par les événements. Lorsqu'un serveur web reçoit une requête, il la traite de manière asynchrone sans bloquer le thread principal, puis passe à la requête suivante.
Lorsque la requête est traitée, son résultat est remis dans la file d'attente. Lorsqu'il atteint le début de la file, le résultat est renvoyé à l'utilisateur.
Ce modèle est très efficace et hautement évolutive, car le serveur web continue d'accepter des requêtes sans attendre la fin des opérations d'E/S (ce que l'on appelle également E/S non bloquante ou E/O dirigée par les événements).
Dans le modèle de programmation dirigée par les événements, une boucle principale est générée pour écouter les événements. Lorsqu'un événement est détecté, la fonction de rappel associée est déclenchée.
L'ensemble du flux de la programmation dirigée par les événements fonctionne ainsi de manière très concise, similaire au motif observateur, où l'événement équivaut à un sujet (Subject) et toutes les fonctions de traitement enregistrées pour cet événement équivalent aux observateurs (Observer).
Node.js possède plusieurs événements intégrés. Nous pouvons importer le module events et instancier la classe EventEmitter pour lier et écouter des événements, comme dans l'exemple suivant :
// Importation du module events
const gestionnaireEvenements = require('events');
// Création d'un objet EventEmitter
const emetteur = new gestionnaireEvenements.EventEmitter();
Le programme suivant lie un gestionnaire d'événements :
// Liaison d'un événement et de son gestionnaire
emetteur.on('nomEvenement', gestionnaireEvenement);
Nous pouvons déclencher un événement par programme :
// Déclenchement de l'événement
emetteur.emit('nomEvenement');
Événements Multiples et Paramètres Multiples :
Chaque événement de l'EventEmitter est composé d'un nom d'événement et de plusieurs paramètres. Le nom d'événement est une chaîne de caractères qui exprime généralement une sémantique précise. Pour chaque événement, l'EventEmitter prend en charge plusieurs écouteurs d'événements.
Lorsqu'un événement est déclenché, les écouteurs d'événements enregistrés pour cet événement sont appelés les uns après les autres, et les paramètres de l'événement sont transmis en tant que paramètres à la fonction de rappel.
// Fichier evenements.js
const gestionnaireEvenements = require('events');
const emetteur = new gestionnaireEvenements.EventEmitter();
emetteur.on('certainsEvenements', function(param1, param2) {
console.log('ecouteur1', param1, param2);
});
emetteur.on('certainsEvenements', function(param1, param2) {
console.log('ecouteur2', param1, param2);
});
emetteur.emit('certainsEvenements', 'valeur param1', 'valeur param2');
Après exécution de ce code, le résultat est le suivant :
ecouteur1 valeur param1 valeur param2
ecouteur2 valeur param1 valeur param2<br></br><br></br>
Dans cet exemple, l'emetteur a enregistré deux écouteurs d'événements pour l'événement certainsEvenements, puis a déclenché cet événement.
Le résultat montre que les fonctions de rappel des deux écouteurs d'événements ont été appelées successivement. C'est l'utilisation la plus simple de l'EventEmitter.
L'EventEmitter fournit plusieurs méthodes, telles que on et emit. La méthode on est utilisée pour lier des fonctions d'événement, et la méthode emit est utilisée pour déclencher un événement. Examinons maintenant plus en détail les méthodes de l'EventEmitter.
Méthodes
| Numéro | Méthode & Description |
|---|---|
| 1 | addListener(evenement, ecouteur) Ajoute un écouteur pour l'événement spécifié à la fin du tableau d'écouteurs. |
| 2 | on(evenement, ecouteur) Enregistre un écouteur pour l'événement spécifié, acceptant une chaîne evenement et une fonction de rappel. serveur.on('connexion', function (flux) { console.log('quelqu\'un s\'est connecté !'); }); |
| 3 | once(evenement, ecouteur) Enregistre un écouteur unique pour l'événement spécifié, c'est-à-dire que l'écouteur ne sera déclenché qu'une seule fois, puis sera immédiatement supprimé. serveur.once('connexion', function (flux) { console.log('Ah, nous avons notre premier utilisateur !'); }); |
| 4 | removeListener(evenement, ecouteur) Supprime un écouteur spécifique d'un événement, l'écouteur doit avoir été enregistré précédemment pour cet événement. Il accepte deux paramètres, le premier étant le nom de l'événement et le deuxième le nom de la fonction de rappel. const rappel = function(flux) { console.log('quelqu\'un s\'est connecté !'); }; serveur.on('connexion', rappel); // ... serveur.removeListener('connexion', rappel); |
| 5 | removeAllListeners([evenement]) Supprime tous les écouteurs de tous les événements. Si un événement est spécifié, supprime tous les écouteurs de cet événement. |
| 6 | setMaxListeners(n) Par défaut, les EventEmitters émettent un avertissement si vous ajoutez plus de 10 écouteurs. La fonction setMaxListeners est utilisée pour modifier le nombre limite par défaut d'écouteurs. |
| 7 | listeners(evenement) Renvoie le tableau des écouteurs pour l'événement spécifié. |
| 8 | emit(evenement, [arg1], [arg2], [...]) Exécute chaque écouteur dans l'ordre, renvoie true si l'événement a des écouteurs enregistrés, sinon false. |
Méthodes de Classe
| Numéro | Méthode & Description |
|---|---|
| 1 | listenerCount(emetteur, evenement) Renvoie le nombre d'écouteurs pour l'événement spécifié. |
| 2 | defaultMaxListeners Renvoie la valeur par défaut du nombre maximal d'écouteurs. |
| 3 | errorMonitor Symbole utilisé pour installer un écouteur supplémentaire pour les événements 'error'. |
Événements Spéciaux
| Numéro | Événement & Description |
|---|---|
| 1 | newListener - evenement - Chaîne, nom de l'événement - ecouteur - Fonction de traitement de l'événement Cet événement est déclenché lors de l'ajout d'un nouvel écouteur. |
| 2 | removeListener - evenement - Chaîne, nom de l'événement - ecouteur - Fonction de traitement de l'événement Supprime un écouteur du tableau d'écouteurs spécifié. Notez que cette opération modifie l'index des écouteurs situés après l'écouteur supprimé. |
| 3 | error Événement spécial qui contient une sémantique d'erreur. Lorsqu'un événement 'error' est déclenché, s'il n'y a pas d'écouteur correspondant, Node.js le traite comme une exception, quitte le programme et affiche le message d'erreur. |
Exemples
// Importation du module events
const gestionnaireEvenements = require('events');
// Création d'un objet EventEmitter
const emetteurEvenements = new gestionnaireEvenements.EventEmitter();
// Création du gestionnaire d'événements
const gestionnaireConnexion = function etablie(){
console.log("connexion réussie");
// Déclenchement de l'événementdonnees_recues
emetteurEvenements.emit('donnees_recues');
}
// Liaison du gestionnaire d'événements de connexion
emetteurEvenements.on("connexion", gestionnaireConnexion);
// Utilisation d'une fonction anonyme pour lier l'événement donnees_recues
emetteurEvenements.on('donnees_recues',function(){
console.log('réception des données réussie.')
})
// Déclenchement de l'événement connexion
emetteurEvenements.emit("connexion")
console.log('événement connexion traité');
Résultat d'exécution:
connexion réussie réception des données réussie. événement connexion traité
Exemple deux:
const gestionnaireEvenements = require('events');
const emetteur = new gestionnaireEvenements.EventEmitter();
// Écouteur #1
const premierEcouteur = function premierEcouteur() {
console.log('Le premier écouteur est exécuté.');
}
// Écouteur #2
const deuxiemeEcouteur = function deuxiemeEcouteur() {
console.log('Le deuxième écouteur est exécuté.');
}
// Liaison de l'événement connexion avec le gestionnaire premierEcouteur
emetteur.addListener('connexion', premierEcouteur);
// Liaison de l'événement connexion avec le gestionnaire deuxiemeEcouteur
emetteur.on('connexion', deuxiemeEcouteur);
const nombreEcouteurs = emetteur.listenerCount('connexion');
console.log(nombreEcouteurs + " écouteurs écoutent l'événement de connexion.");
// Traitement de l'événement connexion
emetteur.emit('connexion');
// Suppression du gestionnaire premierEcouteur
emetteur.removeListener('connexion', premierEcouteur);
console.log("Le premier écouteur n'est plus écouté.");
// Déclenchement à nouveau de l'événement connexion
emetteur.emit('connexion');
nombreEcouteurs = emetteur.listenerCount('connexion');
console.log(nombreEcouteurs + " écouteurs écoutent l'événement de connexion.");
console.log("Programme terminé.");
Le code ci-dessus, lorsqu'il est exécuté, produit le résultat suivant: 2 écouteurs écoutent l'événement de connexion.
Le premier écouteur est exécuté.
Le deuxième écouteur est exécuté.
Le premier écouteur n'est plus écouté.
Le deuxième écouteur est exécuté.
1 écouteur écoute l'événement de connexion.
Programme terminé.<br></br><br></br><br></br>
Gestion des Erreurs
L'EventEmitter définit un événement spécial 'error', qui contient une sémantique d'erreur. Lorsque nous rencontrons une exception, nous déclenchons généralement l'événement 'error'.
Lorsque l'événement 'error' est déclenché, l'EventEmitter stipule que s'il n'y a pas d'écouteur correspondant, Node.js le traite comme une exception, quitte le programme et affiche le message d'erreur.
Nous devons généralement définir des écouteurs pour les objets susceptibles de déclencher des événements 'error', afin d'éviter que l'ensemble du programme ne s'effondre en cas d'erreur. Par exemple :
const gestionnaireEvenements = require('events');
const emetteur = new gestionnaireEvenements.EventEmitter();
emetteur.emit('erreur');
L'exécution affichera l'erreur suivante :
throw e; // erreur process.nextTick, ou 'error' event on first tick
^
Error: Uncaught, unspecified 'error' event.
at EventEmitter.emit (events.js:50:15)
at Object.<anonymous> (/home/byvoid/error.js:5:9)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Array.0 (module.js:479:10)
at EventEmitter._tickCallback (node.js:192:40)
Hériter d'EventEmitter
La plupart du temps, nous n'utilisons pas directement EventEmitter, mais nous l'héritons dans nos objets. Tous les modules de base qui prennent en charge les réponses aux événements, y compris fs, net, http, sont des sous-classes d'EventEmitter.
Pourquoi faire cela ? Il y a deux raisons :
Premièrement, les objets ayant une fonctionnalité spécifique implémentent des événements, ce qui correspond à la sémantique. L'écoute et le déclenchement d'événements devraient être des méthodes d'un objet.
Deuxièmement, le mécanisme des objets JavaScript est basé sur les prototypes et prend en charge une certaine héritage multiple. Hériter d'EventEmitter ne perturbe pas la relation d'héritage existante de l'objet.