J'y pense et c'est ce que j'ai trouvé:
Voyons ce code ci-dessous:
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 quand il s'agit de setTimeout
méthode, cela devient une exécution asynchrone. Mais JS en revient immédiatement et continue à s'exécuter, ce qui est appelé Non-Blocking
ou Async
. Et il continue de travailler sur d'autres, etc.
Les résultats de cette exécution sont les suivants:
acdb
Donc, fondamentalement, le second setTimeout
est terminé en premier et sa fonction de rappel est exécutée plus tôt que la première et cela a du sens.
Nous parlons ici d'une application monothread. JS Engine continue d'exécuter cela et à moins qu'il ne termine la première requête, il ne passera pas à la seconde. Mais la bonne chose est qu'il n'attendra pas les opérations de blocage comme la setTimeout
résolution, il sera donc plus rapide car il accepte les nouvelles demandes entrantes.
Mais mes questions se posent autour des éléments suivants:
# 1: Si nous parlons d'une application monothread, quel mécanisme traite setTimeouts
alors que le moteur JS accepte plus de requêtes et les exécute? Comment le thread unique continue-t-il de travailler sur d'autres demandes? Ce qui fonctionne pendant setTimeout
que d'autres demandes continuent d'arriver et sont exécutées.
# 2: Si ces setTimeout
fonctions sont exécutées dans les coulisses alors que d'autres requêtes arrivent et sont exécutées, qu'est-ce qui exécute les exécutions asynchrones dans les coulisses? Quelle est cette chose dont nous parlons appelé le EventLoop
?
# 3: Mais la méthode entière ne devrait-elle pas être placée dans le EventLoop
pour que tout soit exécuté et que la méthode de rappel soit appelée? C'est ce que je comprends lorsque je parle 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
? Peut-être quelque chose comme le async
mot - clé en C # ou une sorte d'attribut qui indique la méthode que JS Engine prendra est une méthode asynchrone et devrait être traitée en conséquence.
# 4: Mais un article dit tout à fait contrairement à ce que je supposais sur la façon dont les choses pourraient fonctionner:
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 poussée dans la file d'attente. Le moteur JavaScript ne démarre pas le traitement de la boucle d'événements tant que le code après l'exécution d'une fonction asynchrone n'est pas exécuté.
# 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 dans la question numéro 4:
Ma question ici est donc d'obtenir des éclaircissements sur les éléments énumérés ci-dessus?
Réponses:
Il n'y a qu'un seul thread dans le processus de nœud qui exécutera réellement le JavaScript de votre programme. Cependant, dans le nœud lui-même, il existe en fait plusieurs opérations de gestion de threads du mécanisme de boucle d'événements, et cela inclut un pool de threads IO et une poignée d'autres. La clé est que le nombre de ces threads ne correspond pas au nombre de connexions simultanées gérées comme ils le feraient dans un modèle de concurrence de thread par connexion.
Maintenant à propos de "l'exécution de setTimeouts", lorsque vous appelez
setTimeout
, tout ce que fait le nœud est essentiellement de mettre à jour une structure de données de fonctions à exécuter à un moment dans le futur. Il a essentiellement un tas de files d'attente de choses à faire et à chaque "tick" de la boucle d'événements, il en sélectionne un, le supprime de la file d'attente et l'exécute.Une chose clé à comprendre est que le nœud repose sur le système d'exploitation pour la plupart des tâches lourdes. Ainsi, les demandes réseau entrantes sont en fait suivies par le système d'exploitation lui-même et lorsque le nœud est prêt à en gérer 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 "travail" du nœud IO est soit "Hey OS, vous avez une connexion réseau avec des données prêtes à lire?" ou "Hey OS, l'un de mes appels de système de fichiers en suspens a des données prêtes?". Sur la base de son algorithme interne et de la conception de son moteur de boucle d'événements, le nœud sélectionnera un "tick" de JavaScript pour l'exécuter, l'exécuter, puis répéter le processus à nouveau. C'est ce que l'on entend par boucle d'événements. Node est fondamentalement en train de déterminer à tout moment "quel est le prochain petit morceau de JavaScript que je dois exécuter?", Puis de l'exécuter.
setTimeout
ouprocess.nextTick
.Aucun JavaScript n'est exécuté dans les coulisses. Tout le JavaScript de votre programme s'exécute au premier plan, un à la fois. Ce qui se passe dans les coulisses, c'est que le système d'exploitation gère les E / S et le nœud attend que cela soit prêt et le nœud gère sa file d'attente de javascript en attente d'exécution.
Il existe un ensemble fixe de fonctions dans le nœud core qui sont asynchrones car elles effectuent des appels système et que le nœud sait lesquelles elles sont car elles doivent appeler le système d'exploitation ou C ++. Fondamentalement, toutes les E / S du réseau et du système de fichiers ainsi que les interactions de processus enfants seront asynchrones et la SEULE façon dont JavaScript peut amener le nœud à exécuter quelque chose de manière asynchrone est d'appeler l'une des fonctions asynchrones fournies par la bibliothèque principale de nœud. Même si vous utilisez un package npm qui définit sa propre API, afin de générer la boucle d'événements, le code de ce package npm appellera éventuellement l'une des fonctions asynchrones du noyau du nœud et c'est à ce moment que le nœud sait que la graduation est terminée et qu'il peut démarrer l'événement l'algorithme de boucle à nouveau.
Oui, c'est vrai, mais c'est trompeur. L'essentiel est que le modèle normal soit:
Donc oui, vous pouvez totalement bloquer la boucle d'événements en comptant simplement les nombres de 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 coopérative. Chaque tick de JavaScript doit générer la boucle d'événements dans un délai raisonnable, sinon l'architecture globale échoue.
la source
process.nextTick
vssetTimeout
vssetImmediate
est subtilement différente, même si vous ne devriez pas vraiment vous en soucier. J'ai un article de blog appelé setTimeout et des amis qui va plus en détail.Il existe un didacticiel vidéo fantastique de Philip Roberts, qui explique la boucle d'événements javascript de la manière la plus simpliste et la plus conceptuelle. Chaque développeur javascript devrait y jeter un œil.
Voici le lien vidéo sur Youtube.
la source
Ne pensez pas que le processus hôte est monothread, 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 les travailleurs de fond , mais ceux-ci compliquent le scénario ...
Ainsi, tout votre code js s'exécute dans le même thread, et il n'y a aucune possibilité que vous obteniez deux parties différentes de votre code js pour s'exécuter simultanément (vous n'obtenez donc pas de problème de concurrence à gérer).
Le code js en cours d'exécution est le dernier code que le processus hôte a récupéré dans la boucle d'événements. Dans votre code, vous pouvez essentiellement faire deux choses: exécuter des instructions synchrones et planifier des fonctions à exécuter à l'avenir, lorsque certains événements se produisent.
Voici ma représentation mentale (attention: c'est juste ça, je ne connais pas les détails d'implémentation du navigateur!) De votre exemple de code:
Pendant que votre code est en cours d'exécution, un autre thread du processus hôte effectue le suivi de tous les événements système qui se produisent (clics sur l'interface utilisateur, fichiers lus, paquets réseaux reçus, etc.)
Une fois votre code terminé, il est supprimé de la boucle d'événements et le processus hôte revient à le vérifier pour voir s'il reste du code à exécuter. La boucle d'événements contient deux gestionnaires d'événements de plus: un à exécuter maintenant (la fonction justNow) et un autre en une seconde (la fonction inAWhile).
Le processus hôte essaie maintenant de faire correspondre tous les événements qui se sont produits pour voir si des gestionnaires sont enregistrés pour eux. Il a constaté que l'événement attendu par justNow s'est produit, il a donc commencé à exécuter son code. Lorsque la fonction justNow se termine, elle vérifie la boucle d'événements une autre fois, recherchant des gestionnaires d'événements. En supposant que 1 s s'est écoulée, il exécute la fonction inAWhile, et ainsi de suite ...
la source