Boucle d'événements Node.js

La boucle d'événements est le mécanisme central par lequel Node.js gère les opérations d'E/S non bloquantes, permettant à un seul thread de gérer efficacement plusieurs requêtes simultanées.

Node.js est un environnement d'exécution JavaScript mono-thread qui utilise la boucle d'événements pour gérer les opérations asynchrones telles que les lectures de fichiers, les requêtes réseau et les requêtes de base de données.

La boucle d'événements permet à Node.js d'exécuter du code de manière non bloquante, de gérer plusieurs connexions et d'effectuer des opérations d'E/S asynchrones.

La boucle d'événements permet à Node.js de gérer un grand nombre d'opérations d'E/S concurrentes sans provoquer de blocage de thread ; c'est la clé pour que Node.js traite efficacement les requêtes concurrentes.

Phases de la boucle d'événements

La boucle d'événements est divisée en plusieurs phases, chacune traitant des tâches spécifiques. Les phases clés sont les suivantes :

  • Timers: ExécutersetTimeout()etsetInterval()les rappels.
  • I/O Callbacks: Gérer certains rappels d'E/S retardés.
  • Idle, prepare: Utilisé en interne, peu courant.
  • Poll: Récupérer les nouveaux événements d'E/S et exécuter les rappels liés aux E/S.
  • Check: ExécutersetImmediate()les rappels.
  • Close Callbacks: Gérer les rappels de fermeture, tels quesocket.on('close', ...)。

Déroulement de la boucle d'événements

  • Les tâches entrent dans la file d'attente de la boucle d'événements。
  • La boucle d'événements traite dans l'ordre des phases, et chaque phase a sa propre file d'attente de rappels.
  • La boucle d'événements sera dans lapollphase en attente de l'arrivée de nouveaux événements ; s'il n'y a pas d'événements, elle vérifiera les callbacks dans d'autres phases.
  • SisetImmediate()etsetTimeout()les deux existent,setImmediate()Danscheckla phase s'exécute en premier, tandis quesetTimeout()Danstimersla phase s'exécute.

Exemple

setTimeout(() => {
  console.log('Callback de timeout');
}, 0);

setImmediate(() => {
  console.log('Callback immédiat');
});

console.log('Exécution du thread principal');

Ordre de sortie :

  • Main thread executionS'affiche en premier.
  • setImmediate()etsetTimeout()L'ordre d'exécution dépend de l'état actuel de la boucle d'événements ; généralementsetImmediate()s'exécutera en premier.

Macrotâches et microtâches

  • Macrotâches:setTimeout、setInterval、setImmediate, opérations d'E/S, etc.
  • Microtâches:process.nextTick、Promise.then。

Ordre d'exécution :Les microtâches ont une priorité plus élevée que les macrotâches et seront exécutées immédiatement après la fin des fonctions de rappel de la phase actuelle.

Exemple

setTimeout(() => {
  console.log('Callback de timeout');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise callback');
});

console.log('Exécution du thread principal');

Résultat de sortie d'exécution :

Main thread execution
Promise callback
Timeout callback

process.nextTick()

process.nextTick() exécute les microtâches après la fin de l'opération en cours et avant le début de la phase suivante, avec une priorité plus élevée que Promise.

Exemple

process.nextTick(() => {
  console.log('Next tick callback');
});

console.log('Exécution du thread principal');

Sortie :

Main thread execution
Next tick callback

Programme piloté par événements

Dans Node.js, la programmation pilotée par événements est principalement mise en œuvre via la classe EventEmitter.

EventEmitter est une classe intégrée située dans le module events. En étendant EventEmitter, vous pouvez créer votre propre émetteur d'événements et enregistrer et déclencher des événements.

Grâce à ce mécanisme, Node.js peut gérer efficacement des tâches asynchrones et réaliser un traitement concurrent même dans un environnement monothread.

L'ensemble du flux piloté par événements est mis en œuvre exactement de cette manière ; c'est très concis. Cela ressemble quelque peu au modèle observateur : un événement équivaut à un sujet (Subject), et toutes les fonctions de gestion enregistrées pour cet événement sont équivalentes à des observateurs (Observer).

Concepts de base :

  • Événement: Une action ou un changement d'état qui se produit dans un programme, comme la fin d'une lecture de fichier ou l'arrivée d'une requête HTTP.
  • EventEmitter:EventEmitterest un module intégré de Node.js, utilisé pour émettre et écouter des événements.
  • Gestionnaire d'événement: Une fonction de rappel associée à un événement, appelée lorsque l'événement se produit.

Flux piloté par événements :

  • Enregistrer un événement: Dans le programme, viaEventEmitterune instance enregistre des événements et les gestionnaires correspondants.
  • Déclencher l'événement: Lorsque l'événement spécifié se produit,EventEmitteril déclenchera l'événement.
  • Gérer l'événement: La boucle d'événements planifie l'exécution de la fonction de rappel correspondante pour accomplir la tâche.

Node.js possède plusieurs événements intégrés. Nous pouvons lier et écouter des événements en important le module events et en instanciant la classe EventEmitter, comme dans l'exemple suivant :

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

Le programme suivant lie des gestionnaires d'événements :

// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);

Nous pouvons déclencher des événements via le programme :

// 触发事件
eventEmitter.emit('eventName');

Exemple

Créez un fichier hello.js, le code est le suivant :

Exemple 1

const EventEmitter = require('events');
const myEmitter = new EventEmitter();

// Enregistrer le gestionnaire d'événement
myEmitter.on('greet', () => {
  console.log('Hello, world!');
});

// Déclencher l’événement
myEmitter.emit('greet');

Sortie :

Hello, world!

Créez un fichier main.js, le code est le suivant :

Exemple 2

//Importer le module events var events = require('events'); //Créer un objet eventEmitter var eventEmitter = new events.EventEmitter(); //Créer un gestionnaire d'événement var connectHandler = function connected() { console.log('Connexion réussie.'); //Déclencher l'événement data_received eventEmitter.emit('data_received'); } //Lier le gestionnaire de l'événement connection eventEmitter.on('connection', connectHandler); //Utiliser une fonction anonyme pour lier l'événement data_received eventEmitter.on('data_received', function(){ console.log('Données reçues avec succès.'); }); //Déclencher l'événement connection eventEmitter.emit('connection'); console.log("Exécution du programme terminée.");

Ensuite, exécutons le code ci-dessus :

$ node main.js
连接成功。
数据接收成功。
程序执行完毕。

Comment fonctionne une application Node ?

Dans les applications Node, les fonctions qui effectuent des opérations asynchrones prennent une fonction de rappel comme dernier paramètre, et la fonction de rappel reçoit un objet d'erreur comme premier paramètre.

Ensuite, revoyons l'exemple précédent. Créez un fichier input.txt avec le contenu suivant :

Example官网地址:www.example.com

Créez un fichier main.js avec le code suivant :

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
   if (err){
      console.log(err.stack);
      return;
   }
   console.log(data.toString());
});
console.log("程序执行完毕");

Dans le programme ci-dessus, fs.readFile() est une fonction asynchrone utilisée pour lire des fichiers. Si une erreur se produit pendant le processus de lecture du fichier, l'objet d'erreur err affichera le message d'erreur.

Si aucune erreur ne se produit, readFile ignore la sortie de l'objet err, et le contenu du fichier est affiché via la fonction de rappel.

Exécutez le code ci-dessus, le résultat d'exécution est le suivant :

程序执行完毕
Example官网地址:www.example.com

Ensuite, nous supprimons le fichier input.txt, et le résultat d'exécution est le suivant :

程序执行完毕
Error: ENOENT, open 'input.txt'

Comme le fichier input.txt n'existe pas, un message d'erreur est affiché.

Autres extensions