Dans Node, de nombreux objets émettent des événements. Par exemple, un serveur TCP émettra un événement « connect » chaque fois qu'un client demande une connexion. Par exemple, chaque fois qu'un bloc entier de données est lu, le système de fichiers émettra un événement « data ». Ces objets sont appelés émetteurs d'événements dans Node. Les émetteurs d'événements permettent aux programmeurs de s'abonner aux événements qui les intéressent et de lier les fonctions de rappel aux événements pertinents, de sorte que la fonction de rappel soit appelée à chaque fois que l'émetteur d'événements émet un événement. Le modèle de publication/abonnement est très similaire au modèle GUI traditionnel. Par exemple, lorsqu'un bouton est cliqué, le programme recevra la notification correspondante. Grâce à ce mode, le programme serveur peut réagir lorsque certains événements se produisent, comme lorsqu'un client se connecte, que des données sont disponibles sur le socket ou lorsque le fichier est fermé.
Vous pouvez également créer votre propre émetteur d'événements. En fait, Node fournit une pseudo-classe EventEmitter, qui peut être utilisée comme classe de base pour créer votre propre émetteur d'événements.
Comprendre le modèle de rappel
La programmation asynchrone n'utilise pas les valeurs de retour de fonction pour indiquer la fin des appels de fonction, mais adopte le style de livraison successeur.
Le "Continuation-passing style" (CPS : Continuation-passing style) est un style de programmation dans lequel le contrôle de flux est explicitement transmis à l'opération suivante...
La fonction de style CPS acceptera une fonction comme paramètre supplémentaire. Cette fonction est utilisée pour indiquer explicitement le prochain processus contrôlé par le programme. Lorsque la fonction CPS calculera sa "valeur de retour", elle appellera la fonction qui représente le. étape suivante du programme en fonction d'un processus et prend comme paramètre la "valeur de retour" de la fonction CPS.
De Wikipédia - http://en.wikipedia.org/wiki/Continuation-passing_style
Dans ce style de programmation, chaque fonction appellera une fonction de rappel après l'exécution, afin que le programme puisse continuer à s'exécuter. Vous comprendrez plus tard que JavaScript est très adapté à ce style de programmation. Voici un exemple de chargement d'un fichier en mémoire sous Node :
.
fs.readFile('/etc/passwd', function(err, fileContent) {
si (erreur) {
lancez une erreur ;
}
console.log('file content', fileContent.toString());
});
Dans cet exemple, vous transmettez une fonction anonyme en ligne comme deuxième paramètre de fs.readFile. En fait, il s'agit d'une programmation utilisant CPS, car vous confiez le processus ultérieur d'exécution du programme à la fonction de rappel.
Comme vous pouvez le voir, le premier paramètre de la fonction de rappel est un objet d'erreur. Si une erreur se produit dans le programme, ce paramètre sera une instance de la classe Error. Il s'agit d'un modèle courant dans la programmation CPS dans Node.
Comprendre le modèle d'émetteur d'événement
Dans le mode de rappel standard, une fonction est transmise en paramètre à la fonction à exécuter. Ce mode fonctionne bien dans les scénarios où le client doit être averti une fois la fonction terminée. Cependant, ce modèle ne convient pas si plusieurs événements se produisent lors de l'exécution de la fonction ou si l'événement se produit plusieurs fois de manière répétée. Par exemple, vous souhaitez être averti chaque fois que le socket reçoit des données disponibles. Dans ce scénario, vous constaterez que le mode de rappel standard n'est pas très facile à utiliser. Pour le moment, le mode émetteur d'événements est pratique. un ensemble d'interfaces standard pour une séparation claire des générateurs d'événements et des écouteurs d'événements.
Lors de l'utilisation du modèle générateur d'événements, deux objets ou plus sont impliqués : un émetteur d'événement et un ou plusieurs écouteurs d'événement.
L'émetteur d'événements, comme son nom l'indique, est un objet qui peut générer des événements. L'écouteur d'événements est le code lié à l'émetteur d'événements pour écouter des types spécifiques d'événements, comme l'exemple suivant :
réponse.on("données", fonction(données) {
console.log("certaines données de la réponse", data);
});
réponse.on("end", function() {
console.log("réponse terminée");
});
});
req.end();
Ce code montre les deux étapes nécessaires pour créer une requête HTTP pour accéder à un serveur HTTP distant à l'aide de l'API http.request de Node (voir les chapitres suivants). La première ligne utilise le "continuation-passing style" (CPS : Continuation-passing style), en passant une fonction en ligne qui sera appelée lorsque la réponse HTTP sera effectuée. L'API de requête HTTP utilise ici CPS car le programme doit continuer à effectuer les opérations suivantes après l'exécution de la fonction http.request.
Lorsque http.request est exécuté, la fonction de rappel anonyme sera appelée, puis l'objet de réponse HTTP lui sera transmis en paramètre. Cet objet de réponse HTTP est un émetteur d'événements. Selon la documentation de Node, il peut. émettre des données, terminer à Pour de nombreux événements, les fonctions de rappel que vous avez enregistrées seront appelées à chaque fois que l'événement se produit.
En règle générale, utilisez le modèle CPS lorsque vous devez récupérer les droits d'exécution une fois l'opération demandée terminée, et utilisez le modèle Event Emitter lorsqu'un événement peut se produire plusieurs fois.
Comprendre les types d'événements
Les événements émis ont un type représenté par une chaîne. L'exemple précédent comprend deux types d'événements : "data" et "end". Ce sont des chaînes arbitraires définies par l'émetteur de l'événement, mais la convention est que les types d'événements sont généralement constitués de minuscules. mots qui ne contiennent pas de caractères nuls.
Vous ne pouvez pas utiliser de code pour déduire quels types d'événements un émetteur d'événements peut produire, car l'API de l'émetteur d'événements n'a pas de mécanisme d'introspection, donc l'API que vous utilisez doit avoir une documentation pour montrer quels types d'événements elle peut émettre.
Une fois qu'un événement se produit, l'émetteur d'événement appellera l'auditeur lié à l'événement et transmettra les données pertinentes à l'auditeur en tant que paramètre. Dans l'exemple http.request précédent, la fonction de rappel d'événement « data » accepte un objet de données comme premier et unique paramètre, tandis que « end » n'accepte aucune donnée. Ces paramètres sont également déterminés par l'auteur de l'API dans le cadre de l'API. Contract. Définies subjectivement, les signatures des paramètres de ces fonctions de rappel sont également décrites dans la documentation API de chaque émetteur d'événement.
Bien que l'émetteur d'événements soit une interface qui sert tous les types d'événements, l'événement "error" est une implémentation spéciale dans Node. La plupart des émetteurs d'événements dans Node généreront un événement « erreur » lorsqu'une erreur se produit dans le programme. Si le programme n'écoute pas l'événement « erreur » d'un émetteur d'événements, l'émetteur d'événements le remarquera et le lancera lorsqu'une erreur se produit. . Une exception non capturée.
Vous pouvez exécuter le code suivant dans Node PERL pour tester l'effet. Il simule un émetteur d'événements pouvant générer deux événements :
em.emit('event1');
em.emit('erreur', new Erreur('Mon erreur'));
Vous verrez le résultat suivant :
indéfini
>em.emit('event1');
faux
> em.emit('erreur', new Erreur('Mon erreur'));
Erreur : mon erreur
à la repl:1:18
sur REPLServer.eval (repl.js:80:21)
sur repl.js:190:20
sur REPLServer.eval (repl.js:87:5)
sur Interface.
sur Interface.emit (events.js:67:17)
sur Interface._onLine (readline.js:162:10)
sur Interface._line (readline.js:426:8)
sur Interface._ttyWrite (readline.js:603:14)
sur ReadStream.
>
Dans la ligne 2 du code, un événement appelé "event1" est émis aléatoirement, ce qui n'a aucun effet. Cependant, lorsque l'événement "error" est émis, l'erreur est renvoyée dans la pile. Si le programme ne s'exécute pas dans un environnement de ligne de commande PERL, le programme plantera en raison d'exceptions non interceptées.
Utilisation de l'API Event Emitter
Tout objet qui implémente le modèle d'émetteur d'événement (tel que TCP Socket, requête HTTP, etc.) implémente l'ensemble de méthodes suivant :
Nous les présentons en détail ci-dessous.
Utilisez .addListener() ou .on() pour lier la fonction de rappel
En spécifiant le type d'événement et la fonction de rappel, vous pouvez enregistrer les opérations à effectuer lorsque l'événement se produit. Par exemple, s'il existe un bloc de données disponible lorsque le fichier lit le flux de données, un événement "data" sera émis. Le code suivant montre comment laisser le programme vous informer qu'un événement de données s'est produit en passant une fonction de rappel. .
console.log("données obtenues du flux de lecture du fichier : %j", data);
}
readStream.addListener("data", recevoirData);
Vous pouvez également utiliser .on, qui est juste un raccourci pour .addListener. Le code suivant est le même que celui ci-dessus :
console.log("données obtenues du flux de lecture du fichier : %j", data);
}
readStream.on("données", recevoirDonnées);
Le code précédent utilise une fonction nommée prédéfinie comme fonction de rappel. Vous pouvez également utiliser une fonction anonyme en ligne pour simplifier le code :
console.log("données obtenues du flux de lecture du fichier : %j", data);
});
Lier plusieurs auditeurs d'événements
Le modèle d'émetteur d'événement permet à plusieurs écouteurs d'événements d'écouter le même type d'événement du même émetteur d'événement, tel que :
J'ai aussi quelques données ici.
L'émetteur d'événement est chargé d'appeler tous les auditeurs liés au type d'événement spécifié dans l'ordre dans lequel les auditeurs ont été enregistrés, c'est-à-dire :
1. Lorsqu'un événement se produit, l'écouteur d'événement peut ne pas être appelé immédiatement. Il peut y avoir d'autres écouteurs d'événement appelés avant lui.
2. Il est anormal que des exceptions soient émises sur la pile. Cela peut être dû à un bug dans le code. Lorsqu'un événement est émis, si un écouteur d'événement lève une exception lorsqu'il est appelé, cela peut provoquer un événement. les auditeurs ne sont jamais appelés. Dans ce cas, l'émetteur d'événements intercepte l'exception et peut-être la gère.
Regardez cet exemple :
throw new Error("Quelque chose de mal est arrivé");
});
readStream.on("données", fonction(données) {
console.log('J'ai aussi des données ici.');
});
Comme le premier auditeur a lancé une exception, le deuxième auditeur ne sera pas appelé.
Supprimez un écouteur d'événement d'un émetteur d'événement à l'aide de .removeListener()
Si vous ne vous souciez plus d'un événement d'un objet, vous pouvez annuler l'écouteur d'événement enregistré en spécifiant le type d'événement et la fonction de rappel, comme ceci :
console.log("données obtenues du flux de lecture du fichier : %j", data);
}
readStream.on("data", recevoirData);
// ...
readStream.removeListener("data", recevoirData);
Dans cet exemple, la dernière ligne supprime un écouteur d'événement de l'objet émetteur d'événement qui peut être appelé à tout moment dans le futur.
Pour supprimer l'écouteur, vous devez nommer la fonction de rappel, car le nom de la fonction de rappel est nécessaire lors de l'ajout et de la suppression.
Utilisez .once() pour exécuter la fonction de rappel au plus une fois
Si vous souhaitez surveiller un événement qui peut être exécuté au plus une fois, ou si vous êtes intéressé uniquement par la première fois qu'un événement se produit, vous pouvez utiliser la fonction .once() :
console.log("données obtenues du flux de lecture du fichier : %j", data);
}
readStream.once("data", recevoirData);
Dans le code ci-dessus, la fonction recevoirData ne sera appelée qu'une seule fois. Si l'objet readStream émet l'événement data, la fonction de rappel recevoirData ne sera déclenchée qu'une seule fois.
C'est en fait juste une méthode pratique, car elle est très simple à mettre en œuvre, comme ceci :
EventEmitter.prototype.once = function(type, rappel) {
var ça = ceci;
this.on(type, function écouteur() {
that.removeListener(type, écouteur);
callback.apply(cela, arguments);
});
};
Dans le code ci-dessus, vous redéfinissez la fonction EventEmitter.prototype.once, et redéfinissez également la fonction once de chaque objet qui hérite de EventEmitter. Le code utilise simplement la méthode .on() Une fois l'événement reçu, il utilise .removeEventListener() pour annuler l'enregistrement de la fonction de rappel et appeler la fonction de rappel d'origine.
Remarque : La méthode function.apply() est utilisée dans le code précédent, qui accepte un objet et l'utilise comme contenu de cette variable, ainsi qu'un tableau de paramètres. Dans l'exemple précédent, le tableau de paramètres non modifié est transmis de manière transparente à la fonction de rappel via l'émetteur d'événements.
Supprimez tous les écouteurs d'événements de l'émetteur d'événements à l'aide de .removeAllListeners()
Vous pouvez supprimer tous les auditeurs enregistrés sur un type d'événement spécifié d'un émetteur d'événement comme suit :
Par exemple, vous pouvez annuler tous les écouteurs de signal d'interruption de processus comme ceci :
Remarque : en règle générale, il est recommandé de n'utiliser cette fonction que si vous savez exactement ce qui est supprimé. Sinon, vous devez laisser d'autres parties de l'application supprimer la collection d'écouteurs d'événements, ou vous pouvez les laisser. certaines parties de l'application assument la responsabilité d'elles-mêmes. Supprimez l'écouteur. Quoi qu'il en soit, dans certains cas rares, cette fonction reste très utile, par exemple lorsque vous vous préparez à arrêter un émetteur d'événements de manière ordonnée ou à arrêter l'ensemble du processus.
Créer un émetteur d'événement
Les émetteurs d'événements sont un excellent moyen de rendre les interfaces de programmation plus polyvalentes. Dans un modèle de programmation commun et facile à comprendre, le client appelle directement diverses fonctions, tandis que dans le modèle d'émetteur d'événements, le client est lié à divers événements, cela rendra votre programme plus flexible. (Note du traducteur : cette phrase n'est pas très sûre, j'ai donc posté le texte original : L'émetteur d'événements fournit un excellent moyen de rendre une interface de programmation plus générique. Lorsque vous utilisez un modèle commun, les clients se lient aux événements au lieu d'invoquer des fonctions, rendant votre programme plus flexible.)
De plus, en utilisant des émetteurs d'événements, vous pouvez également obtenir de nombreuses fonctionnalités, telles que la liaison de plusieurs auditeurs non liés au même événement.
Héritage de l'émetteur d'événement Node
Si vous êtes intéressé par le modèle d'émetteur d'événements de Node et envisagez de l'utiliser dans votre propre application, vous pouvez créer une pseudo-classe en héritant d'EventEmitter :
var EventEmitter = require('events').EventEmitter;
// Voici le constructeur de MyClass :
var MaClasse = function() {
}
util.inherits(MyClass, EventEmitter);
Remarque : util.inherits établit la chaîne de prototypes de MyClass afin que votre instance MyClass puisse utiliser la méthode prototype de EventEmitter.
Événement de lancement
En héritant de EventEmitter, MyClass peut émettre des événements comme celui-ci :
this.emit("événement personnalisé", "argument 1", "argument 2");
};
Dans le code ci-dessus, lorsque la méthode someMethod est appelée par une instance de MyClass, un événement appelé "événement cuteom" sera émis. Cet événement émettra également deux chaînes comme données : "argument 1" et "argument 2". , ils seront transmis en tant que paramètres à l'écouteur d'événement.
Les clients des instances MyClass peuvent écouter les événements « événements personnalisés » comme celui-ci :
myInstance.on('événement personnalisé', function(str1, str2) {
console.log('j'ai un événement personnalisé avec str1 %s et str2 %s!', str1, str2);
});
Pour un autre exemple, vous pouvez créer une classe Ticker qui émet un événement "tick" chaque seconde :
EventEmitter = require('events').EventEmitter;
var Ticker = fonction() {
var self = ceci;
setInterval(function() {
self.emit('tick');
}, 1000);
};
util.inherits(Ticker, EventEmitter);
Le client utilisant la classe Ticker peut montrer comment utiliser la classe Ticker et écouter l'événement "tick",
ticker.on("tick", function() {
console.log("tick");
});
Résumé
Le modèle émetteur d'événement est un modèle réentrant qui peut être utilisé pour découpler un objet émetteur d'événement d'un ensemble de code spécifique à un événement.
Vous pouvez utiliser event_emitter.on() pour enregistrer un écouteur pour un type d'événement spécifique, et event_emitter.removeListener() pour vous désinscrire.
Vous pouvez également créer votre propre émetteur d'événements en héritant de EventEmitter et en utilisant simplement la fonction .emit().