Démystifions la boucle d’événement (event loop) de Node.js

Temps de lecture : 3 min

Pour ce premier article consacré à Node.js, nous allons nous intéresser à la boucle d’événement ou “event loop” en anglais. On trouve pas mal d’articles en anglais sur internet, mais très peu en français, j’espère donc que cet article aidera les personnes allergiques à la langue de Shakespeare.

Synchrone vs asynchrone

Node.js, contrairement à d’autres plateformes, est monothreadé c’est-à-dire qu’un seul thread est utilisé pour traiter toutes les requêtes et opérations. On peut donc penser que ce n’est pas très efficace, mais Node.js tire sa force de son architecture événementielle et asynchrone, on dit aussi que Node.js est non bloquant. Voyons ceci avec un petit schéma :

Synchrone vs asynchrone

Imaginons que notre programme exécute des requêtes en base de données. Dans le modèle synchrone, le programme exécute une requête et doit attendre que le système de gestion de base de données lui retourne le résultat avant de passer à la requête suivante. Dans le modèle asynchrone, cette fois-ci le programme n’a pas besoin d’attendre le résultat d’une requête avant d’exécuter la suivante, une fois le résultat de la requête retourné au programme, celui-ci effectue les actions qu’on lui avait demandées (fonction de callback) . De manière générale pour toutes interactions extérieures au programme (entrées/sorties), Node.js utilise l’asynchrone. Par exemple :

  • Requête HTTP,
  • Requête en base de données,
  • Lecture/écriture de fichiers sur le disque dur,
  • Envoi d’emails
  • etc.

Bon maintenant on va voir ce qui se cache dernière tout ça et vous vous en doutez il s’agit de l’event loop.

Qu’est ce que l’event loop?

On va tout de suite entrer dans le vif du sujet, l’event loop est le coeur de Node.js, c’est elle qui permet d’effectuer les traitements asynchrones. Elle comporte plusieurs phases :

  • timers : Exécute les callbacks des fonctions setTimeout et setInterval,
  • I/O callbacks : La plupart des callbacks sont exécutés dans cette phase à l’exception des callbacks close, des timers et de setImmediate,
  • idle, prepare : Cette phase est utilisée en interne elle ne nous intéresse donc pas,
  • I/O polling : Récupération ou attente de nouveaux évènements I/O. Si l’application est inactive, c’est-à-dire qu’il n’y a pas de taches en attente (timers, callbacks, etc.) plutôt que de parcourir les différentes phases inutilement, la boucle d’événements reste dans cette phase en attentes de nouveaux événements externes.
  • check : Exécute les callbacks de la fonction setImmediate,
  • close callback : Exécute  les callbacks de fermeture (i.e. : on(‘close’)),
  • nextTick : Cette phase ne fait pas partie de l’event loop, elle exécute les callbacks de la fonction nextTick à la fin de chaque phase de l’event loop.

 

Event loop

Chacune de ces phases possède une file (FIFO) de callback a exécuter. Dès que la file est vide, on passe à la phase suivante.

On va prendre un exemple de code pour bien comprendre comment cela fonctionne :

Voici comment s’exécute ce code :

  1. Le programme démarre, passe les phases “timers” et “I/O callbacks” puisque leurs files sont vides et se retrouve dans la phase “I/O polling”,
  2. La fonction readFile appelle le processus et place le callback dans la file de la phase “I/O callbacks”, il n’y a plus d’événements externes, on passe à la phase suivante,
  3. Les phases “check” et “close callbacks” sont passées puisque leurs files sont vides, un tour de boucle vient donc d’être effectué,
  4. Comme la file de la phase “timers” est vide, on passe à la phase suivante à savoir la phase “I/O callbacks”,
  5. Un callback est en attente (celui de la fonction readFile) celui-ci est exécuté. Les fonctions setTimeout, setImmediate et nextTick sont exécutées (attention pas leurs callbacks),
  6. La phase “I/O callbacks” est terminée, le callback de nextTick est exécuté,
  7. L’event loop est dans la phase “I/O polling” et vérifie s’il y a des événements externes, des timers de terminés ou des callbacks de fonctions setImmediate en attentes. C’est le cas, on a le timer de la fonction setTimeout qui est terminé, son callback est donc placé dans la file de la phase “timer”. On a également le callback de la fonction setImmediate qui est placé dans file de la phase “check”. L’event loop passe ensuite à la phase “check”.
  8. Le callback de la fonction setImmediate est exécuté, il n’y a pas d’autre callback en attente,  l’event loop retourne à la phase “timers” puisque la file de la phase “close callbacks” est vide,
  9. Le callback de la fonction setTimeout est donc exécuté,
  10. L’event loop passe ensuite les phases “check” et “close callbacks” puisque leurs files sont vides et se retrouve dans la phase “I/O polling” en attente d’événements.

On a donc le résultat suivant :

voilà j’espère qu’avec cet article vous y voyez plus clair sur le fonctionnement en interne de Node.js.

Bonus :

Pour les plus courageux d’entre vous, voici une vidéo de Bert Belder, l’un des principaux membres du comité de pilotage technique de Node.js, qui explique plus en détail le fonctionnement de l’event loop :

Laisser un commentaire