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

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 (via des fonctions de callback par exemple) . 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 derriè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 cœur 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;
  • pending callbacks : Cette phase exécute les callbacks de certaines opérations systèmes comme les erreurs TCP par exemple.
  • idle, prepare : Cette phase est utilisée en interne elle ne nous intéresse donc pas;
  • I/O polling : Cette phase permet d’attendre de nouveaux evénements I/O et d’exécuter les callbacks associés. Si tout les callbacks de cette phase ont été exécutés et qu’il n’y a pas de tâches en attente (callbacks d’autres phases ou timers) 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.
  • microTask (promise) : Cette étape ne fait également pas partie de l’event loop. Elle exécute les callbacks associés aux promesses (reject ou resolve) à la fin de chaque phase de l’event loop juste après nextTick.
Event loop de Node.js
Event loop de Node.js

Chacune de ces phases possède une file (FIFO) de callback à 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 et se met en attente dans la phase “I/O polling”;
  2. La fonction readFile est exécutée (attention pas son callback) ;
  3. Une fois la lecture du fichier terminée, son callback est placé dans la file de la phase “I/O polling”. Comme on se trouve dans celle-ci, le callback est exécuté. Les fonctions setTimeout, setImmediate et nextTick sont ensuite exécutées (attention encore une fois pas leurs callbacks),
  4. La phase “I/O callbacks” est terminée, le callback de nextTick est exécuté,
  5. L’event loop vérifie s’il y a des timers de terminés ou des callbacks de fonctions setImmediate en attentes. C’est le cas, il y 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 la file de la phase “check”. L’event loop passe donc à la phase “check”.
  6. 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,
  7. Le callback de la fonction setTimeout est donc exécuté,
  8. L’event loop passe ensuite à la phase “pending”. Sa file étant vide, l’event se retrouve dans la phase “I/O polling” en attente de callbacks.

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 :

Développeur back (nodejs & php), je fais aussi du front (react). Je partage mes connaissances et ma passion au travers de mes articles. N'hésitez pas à me suivre sur Twitter.

2 commentaires

  1. Hello hello, j’apprécie réellement votre article, je pense (enfin) avoir compris le fonction de l’event loop. En tout cas, j’y vois plus clair. Merci merci

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.