web-dev-qa-db-fra.com

Comprendre la boucle d'événement

J'y réfléchis et voici ce que j'ai proposé:

Disons que nous avons un code comme celui-ci:

console.clear();
console.log("a");
setTimeout(function(){console.log("b");},1000);
console.log("c");
setTimeout(function(){console.log("d");},0);

Une demande arrive et le moteur JS commence à exécuter le code ci-dessus étape par étape. Les deux premiers appels sont des appels de synchronisation. Mais lorsqu'il s'agit de la méthode setTimeout, elle devient une exécution asynchrone. Mais JS en revient immédiatement et continue à exécuter, ce qui s'appelle Non-Blocking ou Async. Et il continue de travailler sur d'autres, etc.

Le résultat de cette exécution est le suivant:

a c d b

Donc, fondamentalement, le second setTimeout a été terminé en premier et sa fonction de rappel est exécutée plus tôt que le premier, ce qui est logique.

Nous parlons ici d'une application à un seul thread. JS Engine continue de l'exécuter et, à moins que la première demande ne soit terminée, il ne passera pas à la deuxième. Mais le bon côté des choses est qu’il n’attendra pas que des opérations de blocage telles que setTimeout soient résolues, ce qui accélérera le processus car il accepte les nouvelles demandes entrantes.

Mais mes questions se posent autour des éléments suivants:

# 1: Si nous parlons d'une application à un seul thread, quel mécanisme traite-t-il setTimeouts alors que le moteur JS accepte plus de demandes et les exécute? Comment le thread unique continue-t-il à travailler sur d'autres requêtes? Ce qui fonctionne sur setTimeout alors que d’autres requêtes arrivent et s’exécutent.

# 2: Si ces fonctions setTimeout sont exécutées dans les coulisses alors que d'autres requêtes entrent et sont en cours d'exécution, ce qui exécute l'async exécutions dans les coulisses? Quelle est cette chose dont nous parlons appelée le EventLoop?

# 3: Mais la méthode entière ne devrait pas être placée dans le EventLoop afin que tout soit exécuté et que la méthode de rappel soit appelé? Voici ce que je comprends en parlant des fonctions de rappel:

function downloadFile(filePath, callback)
{
  blah.downloadFile(filePath);
  callback();
}

Mais dans ce cas, comment le moteur JS sait-il s’il s’agit d’une fonction asynchrone afin de pouvoir placer le rappel dans le EventLoop? Perhaps something like theasync` mot-clé en C # ou une sorte d'attribut indiquant que la méthode que JS Engine utilisera est une méthode asynchrone et doit être traité en conséquence.

# 4: Mais un article dit tout à fait contrairement à ce que je devinais sur la façon dont les choses pourraient fonctionner:

La boucle d'événement est une file d'attente de fonctions de rappel. Lorsqu'une fonction asynchrone s'exécute, la fonction de rappel est insérée dans la file d'attente. Le moteur JavaScript ne commence pas à traiter la boucle d'événements avant le code après l'exécution d'une fonction asynchrone.

# 5: Et il y a cette image ici qui pourrait être utile, mais la première explication de l'image dit exactement la même chose que celle mentionnée à la question n ° 4:

enter image description here

Ma question est donc d'obtenir des éclaircissements sur les éléments énumérés ci-dessus?

122
Tarik

1: Si nous parlons d'une application à un seul thread, alors quel processus traite setTimeouts alors que le moteur JS accepte plus de demandes et les exécute? Est-ce que ce thread unique continuera à travailler sur d'autres requêtes? Ensuite, qui va continuer à travailler sur setTimeout pendant que les autres demandes continuent à être exécutées.

Il n'y a qu'un seul thread dans le processus de nœud qui exécute le code JavaScript de votre programme. Cependant, dans le noeud lui-même, il existe en réalité plusieurs threads qui gèrent le mécanisme de la boucle d'événement, ce qui inclut un pool de threads IO et une poignée d'autres. La clé est le nombre de ces threads. ne correspond pas au nombre de connexions simultanées traitées comme dans un modèle de simultanéité thread-per-connection.

En ce qui concerne "executing setTimeouts", lorsque vous appelez setTimeout, tout nœud ne met à jour qu'une structure de données de fonctions à exécuter ultérieurement. En gros, il a un tas de files de choses à faire et chaque "tick" de la boucle d'événement sélectionné, il le supprime de la file d'attente et l'exécute.

Un élément clé à comprendre est que le nœud dépend du système d'exploitation pour la plupart des tâches lourdes. Ainsi, les demandes réseau entrantes sont réellement suivies par le système d'exploitation lui-même et lorsque le noeud est prêt à en traiter une, il utilise simplement un appel système pour demander au système d'exploitation une demande réseau avec des données prêtes à être traitées. Une grande partie du nœud IO "work" est soit "Hey OS, avez-vous une connexion réseau avec des données prêtes à la lecture?" Ou "Hey OS, l'un de mes appels de système de fichiers en attente a des données Ready ? ". En fonction de la conception de son algorithme interne et de son moteur de boucle d'événements, le noeud sélectionnera un" tick "de JavaScript à exécuter, puis l'exécutera à nouveau. C'est ce que l'on entend par boucle d'événement. Node détermine à tout moment "quel est le prochain bit de JavaScript que je devrais exécuter?", Puis l'exécute. Ce facteur dans lequel IO le système d'exploitation est terminé et les choses qui ont été mises en file d'attente dans JavaScript via des appels à setTimeout ou process.nextTick.

2: Si ces setTimeout sont exécutés dans les coulisses alors que d’autres requêtes arrivent et s’exécutent, l’exécution asynchrone des coulisses est-elle celle dont nous parlons EventLoop?

Aucun JavaScript n'est exécuté dans les coulisses. Tout le code JavaScript de votre programme est au premier plan, un à la fois. Ce qui se passe en coulisse, c’est que le système d’exploitation gère IO et le noeud attend que celui-ci soit prêt et le noeud gère sa file d’attente javascript en attente d’exécution.

3: Comment JS Engine peut-il savoir s’il s’agit d’une fonction asynchrone afin de pouvoir la placer dans EventLoop?

Il existe un ensemble fixe de fonctions asynchrones dans le noyau de nœud, car elles effectuent des appels système. Le nœud sait quelles sont ces fonctions car elles doivent appeler le système d'exploitation ou C++. Fondamentalement, tous les réseaux et systèmes de fichiers IO ainsi que les interactions de processus enfants seront asynchrones et le SEUL moyen par lequel JavaScript peut faire en sorte que le noeud s'exécute de manière asynchrone en invoquant l'une des fonctions async fournies par la bibliothèque principale du noeud. Même si vous utilisez un paquet npm qui définit sa propre API, afin de générer la boucle d’événements, le code de ce paquet npm appellera l’une des fonctions asynchrones du cœur du noeud. algorithme de boucle d'événement à nouveau.

4 La boucle d'événements est une file d'attente de fonctions de rappel. Lorsqu'une fonction asynchrone s'exécute, la fonction de rappel est insérée dans la file d'attente. Le moteur JavaScript ne commence pas à traiter la boucle d'événements avant le code après l'exécution d'une fonction asynchrone.

Oui, c'est vrai, mais c'est trompeur. L’essentiel est que le schéma normal soit le suivant:

//Let's say this code is running in tick 1
fs.readFile("/home/barney/colors.txt", function (error, data) {
  //The code inside this callback function will absolutely NOT run in tick 1
  //It will run in some tick >= 2
});
//This code will absolutely also run in tick 1
//HOWEVER, typically there's not much else to do here,
//so at some point soon after queueing up some async IO, this tick
//will have nothing useful to do so it will just end because the IO result
//is necessary before anything useful can be done

Alors oui, vous pouvez bloquer totalement la boucle d'événements en comptant simplement les numéros Fibonacci de manière synchrone, tous en mémoire, dans le même tick, et oui, cela gèlerait totalement votre programme. C'est la concurrence simultanée. Chaque tick de JavaScript doit générer la boucle d'événement dans un délai raisonnable, sinon l'architecture globale échoue.

81
Peter Lyons

Philip Roberts propose un fantastique didacticiel vidéo qui explique la boucle d’événement javascript de la manière la plus simpliste et conceptuelle qui soit. Chaque développeur javascript devrait avoir un coup d'oeil.

Voici la vidéo lien sur Youtube.

56
sam100rav

Ne pensez pas que le processus hôte est à thread unique, ils ne le sont pas. Ce qui est mono-thread est la partie du processus hôte qui exécute votre code javascript.

Sauf pour travailleurs d'arrière-plan , mais cela complique le scénario ...

Ainsi, tout votre code js s'exécute dans le même thread, et il est impossible que deux parties différentes de votre code js s'exécutent simultanément (vous n'avez donc pas à gérer la concomitance).

Le code js en cours d'exécution est le dernier code que le processus hôte a extrait de la boucle d'événements. Dans votre code, vous pouvez faire essentiellement deux choses: exécuter des instructions synchrones et planifier des fonctions à exécuter ultérieurement, lorsque certains événements se produisent.

Voici ma représentation mentale (attention: c'est juste que, je ne connais pas les détails d'implémentation du navigateur!) De votre exemple de code:

console.clear();                                   //exec sync
console.log("a");                                  //exec sync
setTimeout(                //schedule inAWhile to be executed at now +1 s 
    function inAWhile(){
        console.log("b");
    },1000);    
console.log("c");                                  //exec sync
setTimeout(
    function justNow(){          //schedule justNow to be executed just now
        console.log("d");
},0);       

Pendant que votre code est en cours d'exécution, un autre thread du processus hôte assure le suivi de tous les événements système en cours (clics sur l'interface utilisateur, lecture de fichiers, paquets réseau reçus, etc.).

Lorsque votre code est terminé, il est supprimé de la boucle d'événements et le processus hôte revient à la vérifier pour vérifier s'il y a plus de code à exécuter. La boucle d'événement contient deux gestionnaires d'événement supplémentaires: un à exécuter maintenant (la fonction justNow) et un autre dans la seconde (la fonction inAWhile).

Le processus hôte tente maintenant de faire correspondre tous les événements survenus pour voir s’il existe des gestionnaires enregistrés pour eux. Il a constaté que l'événement que justNow attend est arrivé et il commence à exécuter son code. Lorsque la fonction justNow se termine, elle vérifie la boucle d'événements une autre fois, en recherchant les gestionnaires d'événements. En supposant que 1 s soit passé, il exécute la fonction inAWhile, etc.

10
Andrea Parodi