Event Loop Node.js expliqué aux débutants

Backend2026-09-14TryQuickToolBox

Vous avez probablement entendu que Node.js est mono-thread et utilise une boucle d'événements pour gérer des milliers de connexions simultanées. Mais que cela signifie-t-il réellement ? Si vous débutez avec Node.js, la boucle d'événements peut sembler une boîte noire mystérieuse. Cet article la décompose en langage clair, pour que vous puissiez écrire du code Node.js meilleur et plus rapide.

Pourquoi la boucle d'événements existe

Les serveurs web traditionnels (comme Apache avec PHP) créent un nouveau thread ou processus pour chaque requête. Cela fonctionne, mais ne passe pas bien à l'échelle lorsque vous avez de nombreuses connexions simultanées—chaque thread consomme de la mémoire et du CPU. Node.js adopte une approche différente : il exécute votre JavaScript dans un seul thread et utilise une boucle d'événements pour gérer les opérations d'E/S de manière asynchrone. Cela signifie que votre code n'attend pas qu'une requête de base de données ou une lecture de fichier se termine ; il continue d'exécuter d'autres tâches et revient lorsque le résultat est prêt.

Les composants principaux

Avant de plonger dans la boucle d'événements, clarifions les acteurs clés :

Comment fonctionne la boucle d'événements : un exemple simple

Considérez ce code :

console.log('Start');

setTimeout(() => {
  console.log('Timeout');
}, 0);

console.log('End');

Quelle est la sortie ? Si vous avez deviné Start, End, Timeout, vous avez raison. Même si le délai est de 0 milliseconde, le callback n'est pas exécuté immédiatement. Voici ce qui se passe :

  1. console.log('Start') est empilé dans la pile d'appels, exécuté, puis dépilé.
  2. setTimeout est appelé. Node.js enregistre le timer et le configure pour expirer après 0ms. Le callback est stocké, et la fonction retourne.
  3. console.log('End') est empilé, exécuté, puis dépilé.
  4. Maintenant la pile d'appels est vide. La boucle d'événements vérifie la phase des timers et voit que le timer a expiré. Elle déplace le callback vers la pile d'appels, qui affiche Timeout.

Cela démontre la nature non bloquante : le timer ne bloque pas le reste du code.

Les phases de la boucle d'événements

La boucle d'événements traite les callbacks en plusieurs phases, chacune ayant un objectif spécifique. Comprendre ces phases vous aide à prédire l'ordre d'exécution.

Phase Description
Timers Exécute les callbacks planifiés par setTimeout() et setInterval().
Pending Callbacks Exécute les callbacks d'E/S différés à l'itération suivante de la boucle.
Idle, Prepare Utilisé en interne par Node.js.
Poll Récupère les nouveaux événements d'E/S ; exécute les callbacks liés aux E/S (presque tous sauf les timers, setImmediate et les callbacks de fermeture).
Check Exécute les callbacks planifiés par setImmediate().
Close Callbacks Exécute les callbacks de fermeture, par exemple socket.on('close', ...).

Entre chaque phase, Node.js vérifie les microtâches : les callbacks process.nextTick() et les Promesses. Celles-ci ont une priorité plus élevée et sont exécutées immédiatement après la fin de l'opération en cours, avant de passer à la phase suivante.

Microtâches : nextTick et Promesses

Les microtâches ne font pas partie des phases de la boucle d'événements ; elles sont traitées après chaque phase et après chaque callback. Cela les fait s'exécuter avant les timers et les callbacks d'E/S. Par exemple :

setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
process.nextTick(() => console.log('nextTick'));
console.log('Sync');

Sortie : Sync, nextTick, Promise, Timeout. Le code synchrone s'exécute en premier, puis les microtâches (nextTick avant les Promesses), puis le timer.

Attention : Les appels récursifs à process.nextTick() peuvent affamer la boucle d'événements, empêchant les E/S de se produire. Utilisez setImmediate() pour les opérations récursives.

setImmediate vs setTimeout

setImmediate() est conçu pour exécuter un callback immédiatement après la fin de la phase de poll en cours. En revanche, setTimeout() avec 0ms attend la prochaine phase de timers. L'ordre entre eux peut varier selon le contexte, mais à l'intérieur d'un callback d'E/S, setImmediate() s'exécute toujours en premier.

Pourquoi cela compte pour votre code

Comprendre la boucle d'événements vous aide à éviter les pièges courants :

Exemple pratique : lecture de fichier non bloquante

Voici comment la boucle d'événements permet des E/S non bloquantes :

const fs = require('fs');

console.log('Before read');

fs.readFile('large-file.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log('File read complete');
});

console.log('After read');

Sortie : Before read, After read, puis File read complete. La lecture du fichier se fait en arrière-plan, et le callback est mis en file d'attente une fois terminé. Pendant ce temps, d'autres codes peuvent s'exécuter.

FAQ

Node.js est-il vraiment mono-thread ?

Node.js exécute votre JavaScript dans un seul thread, mais il utilise plusieurs threads dans le pool de threads libuv pour les E/S de fichiers, le DNS et d'autres opérations. Donc ce n'est pas entièrement mono-thread sous le capot.

Quelle est la différence entre la pile d'appels et la boucle d'événements ?

La pile d'appels exécute le code synchrone. La boucle d'événements gère les callbacks asynchrones, les déplaçant des files d'attente vers la pile d'appels lorsqu'elle est vide.

Puis-je créer plusieurs boucles d'événements ?

Non, chaque processus Node.js a une seule boucle d'événements. Cependant, vous pouvez utiliser des threads de travail pour exécuter des threads JavaScript séparés, chacun avec sa propre boucle d'événements.

Si vous travaillez avec des logs de serveur pour déboguer des problèmes de performance, essayez notre Analyseur de logs Nginx pour analyser et visualiser rapidement les données de logs.