Qu’est-ce que la boucle d’événements en JavaScript ?

La boucle d’événements est une source de confusion pour de nombreux développeurs, mais c’est une pièce fondamentale du moteur JavaScript. C’est ce qui permet à JavaScript d’être monofilaire, tout en étant capable de s’exécuter de manière non bloquante. Pour comprendre la boucle d’événement, nous devons d’abord expliquer quelques éléments du moteur JavaScript, tels que la pile d’appels, les tâches, les micro-tâches et leurs files d’attente respectives. Décortiquons-les un par un.

La pile d’appels

Le site Pile d’appels est une structure de données qui garde la trace de l’exécution du code JavaScript. Comme son nom l’indique, il s’agit d’une pile, donc d’une structure de données LIFO (Last In, First Out) en mémoire. Chaque fonction exécutée est représentée par un cadre dans la pile d’appels et placée au-dessus de la fonction précédente.

Examinons un exemple simple, étape par étape :

function foo() {
  console.log('foo');
  bar();
}

function bar() {
  console.log('bar');
}

  1. La pile d’appels est initialement vide.
  2. La fonction foo() est poussée sur la pile d’appels.
  3. La fonction foo() est exécutée et retirée de la pile d’appels.
  4. La fonction console.log('foo') est poussée sur la pile d’appels.
  5. La fonction console.log('foo') est exécutée et retirée de la pile d’appels.
  6. La fonction bar() est poussée sur la pile d’appels.
  7. La fonction bar() est exécutée et retirée de la pile d’appels.
  8. La fonction console.log('bar') est poussée sur la pile d’appels.
  9. La fonction console.log('bar') est exécutée et retirée de la pile d’appels.
  10. La pile d’appels est maintenant vide.

Tâches et file d’attente des tâches

Tâches Les tâches sont des blocs de code synchrones et planifiés. Pendant leur exécution, elles ont un accès exclusif à la pile d’appels et peuvent également mettre en file d’attente d’autres tâches. Entre les tâches, le navigateur peut effectuer des mises à jour du rendu. Les tâches sont stockées dans le répertoire File d’attente des tâchesqui attendent d’être exécutées par leurs fonctions associées. La file d’attente des tâches, quant à elle, est une structure de données FIFO (First In, First Out). Parmi les exemples de tâches, on peut citer la fonction de rappel d’un écouteur d’événements associé à un événement et le rappel de la fonction d’écoute d’un événement. setTimeout().

Les micro-tâches et la file d’attente des micro-tâches

Microtâches sont similaires aux tâches en ce sens qu’il s’agit de blocs de code synchrones et planifiés ayant un accès exclusif à la pile d’appels pendant leur exécution. De plus, elles sont stockées dans leur propre structure de données FIFO (First In, First Out), le fichier File d’attente des microtâches. Les micro-tâches diffèrent toutefois des tâches en ce sens que la file d’attente des micro-tâches doit être vidée après la fin d’une tâche et avant un nouveau rendu. Voici quelques exemples de micro-tâches Promise les callbacks et MutationObserver callbacks.

Les micro-tâches et la file d’attente des micro-tâches sont également appelées Jobs et file d’attente des jobs.

La boucle d’événements

Enfin, la Boucle d’événement est une boucle qui continue à fonctionner et vérifie si la pile d’appels est vide. Elle traite les tâches et micro-tâches en les plaçant dans la pile d’appels une par une et contrôle également le processus de rendu. Elle se compose de quatre étapes clés :

  1. Évaluation du script : Exécute de manière synchrone le script jusqu’à ce que la pile d’appels soit vide.
  2. Traitement des tâches : Sélectionnez la première tâche dans la file d’attente des tâches et exécutez-la jusqu’à ce que la pile d’appels soit vide.
  3. Traitement des micro-tâches : Sélectionnez la première micro-tâche dans la file d’attente des micro-tâches et exécutez-la jusqu’à ce que la pile d’appels soit vide, en répétant jusqu’à ce que la file d’attente des micro-tâches soit vide.
  4. Rendu : Rendu de l’interface utilisateur et retour à l’étape 2.

Un exemple pratique

Pour mieux comprendre la boucle d’événement, examinons un exemple pratique, incorporant tous les concepts ci-dessus :

console.log('Script start');

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

Promise.resolve()
  .then(() => console.log('Promise.then() #1'))
  .then(() => console.log('Promise.then() #2'));

console.log('Script end');






Le résultat ressemble-t-il à ce que vous attendiez ? Décomposons ce qui se passe, étape par étape :

  1. La pile d’appels est initialement vide. La boucle d’événement commence à évaluer le script.
  2. console.log() est poussé vers la pile d’appels et exécuté, en enregistrant 'Script start'.
  3. setTimeout() est poussé vers la pile d’appels et exécuté. Cela crée une nouvelle tâche pour sa fonction de rappel dans la file d’attente des tâches.
  4. Promise.prototype.resolve() est poussé vers la pile d’appels et exécuté, appelant à son tour Promise.prototype.then().
  5. Promise.prototype.then() est poussé vers la pile d’appels et exécuté. Cela crée une nouvelle micro-tâche pour sa fonction de rappel dans la file d’attente des micro-tâches.
  6. console.log() est poussé vers la pile d’appels et exécuté, ce qui crée une nouvelle microtâche pour sa fonction de rappel dans la file d’attente des microtâches. 'Script end'.
  7. La boucle d’événement a terminé sa tâche actuelle, l’évaluation du script. Elle commence alors à exécuter la première micro-tâche de la file d’attente des micro-tâches, qui est le callback de la fonction Promise.prototype.then() qui a été mis en file d’attente à l’étape 5.
  8. console.log() est poussé vers la pile d’appels et exécuté, en enregistrant 'Promise.then() #1'.
  9. Promise.prototype.then() est poussé vers la pile d’appels et exécuté. Cela crée une nouvelle entrée pour sa fonction de rappel dans la file d’attente des microtâches.
  10. La boucle d’événement vérifie la file d’attente des micro-tâches. Comme elle n’est pas vide, elle exécute la première microtâche, qui est le callback de la fonction Promise.prototype.then() qui a été mis en file d’attente à l’étape 10.
  11. console.log() est poussée vers la pile d’appels et exécutée, en enregistrant 'Promise.then() #2'.
  12. Un nouveau rendu aurait lieu ici, s’il y en a un.
  13. La file d’attente des micro-tâches étant vide, la boucle d’événement se déplace vers la file d’attente des tâches et exécute la première tâche, qui est le callback de setTimeout() qui a été mise en file d’attente à l’étape 3.
  14. console.log() est poussé vers la pile d’appels et exécuté, en enregistrant 'setTimeout()'.
  15. Un nouveau rendu aurait lieu ici, s’il y en a un.
  16. La pile d’appels est maintenant vide.

Résumé

  • Le site Boucle d’événements est responsable de l’exécution du code JavaScript. Elle évalue et exécute d’abord le script, puis traite Tâches et Microtâches.
  • Tâches et Microtâches sont des blocs de code synchrones et planifiés. Elles sont exécutées une à la fois et sont placées dans le répertoire de l’utilisateur. File d’attente des tâches et File d’attente de micro-tâchesrespectivement.
  • Pour tous ces cas, le Pile d’appels est utilisée pour garder la trace des appels de fonction.
  • Chaque fois que Microtâches sont exécutées, les File d’attente des microtâches doit être vidée avant que la prochaine Tâche peut être exécutée.
  • Rendu se produit entre Tâchesmais pas entre Microtâches.

Notes

  • L’étape d’évaluation du script de la boucle d’événement est en soi traitée de la même manière qu’une tâche.
  • Le second argument de setTimeout() indique un temps minimum avant l’exécution, et non un temps garanti. Ceci est dû au fait que les Tâches s’exécutent dans l’ordre et que des Microtâches peuvent être exécutées entre elles.
  • Le comportement de la boucle d’événement dans Node.js est similaire, mais présente quelques différences. En particulier, il n’y a pas d’étape de rendu.
  • Les anciennes versions du navigateur ne respectaient pas complètement l’ordre des opérations, de sorte que les tâches et les micro-tâches peuvent s’exécuter dans des ordres différents.

Laisser un commentaire