Maison > Article > interface Web > Explication détaillée d'exemples de boucles d'événements js
J'ai déjà lu quelques blogs sur les boucles d'événements, mais après ne pas les avoir lus pendant un moment, j'ai réalisé que je les avais tous oubliés, alors j'ai décidé d'écrire un blog pour les résumer !
Pour autant que nous le sachions, le js du navigateur est monothread, c'est-à-dire disons, au plus un seul segment de code s'exécute en même temps, mais le navigateur peut très bien gérer les requêtes asynchrones, alors pourquoi ? Regardons d'abord une image
À partir de l'image ci-dessus, nous pouvons voir que le thread principal js a une pile d'exécution et que tout le code js s'exécutera dans la pile d'exécution. Lors de l'exécution du code, si vous rencontrez du code asynchrone (tel que setTimeout, ajax, promise.then et les clics de l'utilisateur, etc.), alors le navigateur mettra ces codes dans un thread (ici nous l'appelons un derrière-le -scenes thread) L'attente ne bloque pas l'exécution du thread principal. Le thread principal continue d'exécuter le code restant dans la pile lorsque le code du thread d'arrière-plan est prêt (par exemple, le délai setTimeout est écoulé et la requête ajax. reçoit une réponse), le thread le traitera. La fonction de rappel est placée dans la file d'attente des tâches en attente d'exécution. Lorsque le thread principal aura fini d'exécuter tout le code de la pile, il vérifiera s'il y a une tâche à exécuter dans la File d'attente des tâches S'il y a une tâche à exécuter, alors la tâche sera mise. dans la pile d'exécution pour l'exécution. Si la file d'attente des tâches actuelle est vide, elle continuera d'attendre en boucle que la tâche arrive. C’est ce qu’on appelle une boucle d’événements.
En fait (comme le montre l'image ci-dessus), js a deux files d'attente de tâches, l'une s'appelle Macrotask Queue (Task Queue) et l'autre s'appelle Microtask Queue
Le premier est principalement utilisé pour effectuer des travaux à relativement grande échelle, les plus courants incluent setTimeout, setInterval, les opérations d'interaction utilisateur, le rendu de l'interface utilisateur, etc.
Le second est principalement utilisé pour effectuer des travaux à relativement petite échelle, ce qui est courant. Il existe Promise, process.nextTick (nodejs)
Alors, quelles sont les différences spécifiques entre les deux ? , si deux tâches apparaissent en même temps, laquelle faut-il choisir ?
En fait, ce que fait la boucle d'événements est la suivante :
Vérifier si la file d'attente des macrotâches est vide . Sinon, passez à l'étape suivante. Si elle est vide, passez à 3
Prendre la tâche en tête de file d'attente (avec le temps d'attente le plus long) depuis la liste. File d'attente Macrotask et exécutez-la dans la pile d'exécution (une seule). Après l'exécution, passez à l'étape suivante
Vérifiez si la file d'attente Microtask est vide, sinon passez à l'étape suivante. , sinon, passez à 1 (démarrez une nouvelle boucle d'événements)
Obtenir de la file d'attente Microtask La tâche en tête de file d'attente (avec le temps le plus long dans la file d'attente) va dans la file d'attente des événements pour l'exécution. Après l'exécution, passe à 3
Parmi eux, la tâche de microtâche ajoutée lors de l'exécution du code sera dans la période actuelle de la boucle d'événement. , et la tâche de macrotâche nouvellement ajoutée ne peut attendre que la prochaine boucle d'événement pour s'exécuter (une boucle d'événement n'exécute qu'une seule macrotâche)
Tout d'abord, regardons un morceau de code
console.log(1) setTimeout(function() { //settimeout1 console.log(2) }, 0); const intervalId = setInterval(function() { //setinterval1 console.log(3) }, 0) setTimeout(function() { //settimeout2 console.log(10) new Promise(function(resolve) { //promise1 console.log(11) resolve() }) .then(function() { console.log(12) }) .then(function() { console.log(13) clearInterval(intervalId) }) }, 0); //promise2 Promise.resolve() .then(function() { console.log(7) }) .then(function() { console.log(8) }) console.log(9)
Que voulez-vous pensez-vous que le résultat devrait être ?
Les résultats que je génère dans l'environnement de nœud et la console Chrome sont les suivants :
1 9 7 8 2 3 10 11 12 13
Dans l'exemple ci-dessus
la première boucle d'événement :
console.log(1) est exécuté, sortie 1
settimeout1 est exécuté et ajouté à la file d'attente des macrotâches
setinterval1 est exécuté et ajouté à la file d'attente des macrotâches
settimeout2 est exécuté et ajouté à la file d'attente des macrotâches
promise2 est exécuté et ses deux La fonction then est ajoutée à la file d'attente des microtâches
console.log(9) est exécutée et la sortie est 9
Selon la définition de la boucle d'événements, new sera exécuté ensuite. Les tâches de microtâches ajoutées sont exécutées dans l'ordre d'entrée dans la file d'attente. Console.log(7) et console.log(8) sont affichés. sont affichés. La file d'attente des microtâches est vide. Revenez à la première étape et entrez dans la boucle d'événements suivante. À ce stade, la file d'attente des macrotâches est : settimeout1, setinterval1, settimeout2
.
Depuis la file d'attente des microtâches, prenez la tâche en tête de file d'attente et exécutez-la jusqu'à ce qu'elle soit vide. Par conséquent, les deux tâches de microtâches nouvellement ajoutées sont exécutées en séquence, sorties 12 et 13, et setinterval1 est effacé
À ce moment, la file d'attente des microtâches et la file d'attente des macrotâches sont vides, et le navigateur vérifiera toujours si la file d'attente est vide et attends le nouveau. La tâche est ajoutée à la file d'attente.
Ici, vous vous demandez peut-être, dans la première boucle, pourquoi la macrotâche n'est-elle pas exécutée en premier ? Car selon le processus, ne devrions-nous pas d'abord vérifier si la file d'attente des macrotâches est vide, puis vérifier la file d'attente des microtâches ?
Raison : Parce que la tâche exécutée dans le thread principal js au début est la tâche de macrotâche, et selon le processus de la boucle d'événement, une seule tâche de macrotâche sera exécutée dans une boucle d'événement. Par conséquent, après l'exécution du code. du thread principal, il ira de Prenez la première tâche de la file d'attente des microtâches et exécutez-la.
Lors de l'exécution d'une tâche de microtâche, elle n'entrera dans la boucle d'événements suivante que lorsque la file d'attente des microtâches est vide. Par conséquent, si elle génère continuellement de nouvelles. Les tâches de microtâches entraîneront l'exécution de tâches de microtâches par le thread principal, et il n'y a aucun moyen d'exécuter des tâches de macrotâches. De cette façon, nous ne pourrons pas effectuer de rendu d'interface utilisateur/opérations d'E/S/requêtes ajax. Par conséquent, nous devrions éviter cette situation. . se produire. Dans process.nexttick dans nodejs, vous pouvez définir le nombre maximum d'appels pour éviter de bloquer le thread principal.
Avec cela, nous introduisons un nouveau problème, le problème de la minuterie. La minuterie est-elle réelle et fiable ? Par exemple, si j'exécute une commande : setTimeout(task, 100), sera-t-elle exécutée exactement après 100 millisecondes ? En fait, sur la base de la discussion ci-dessus, nous pouvons savoir que cela est impossible.
Je pense que tout le monde devrait connaître la raison, car après avoir exécuté setTimeout(task,100), vous vous assurez simplement que la tâche entrera dans la file d'attente des macrotâches après 100 millisecondes, mais cela ne signifie pas qu'elle peut s'exécuter immédiatement, peut-être Le thread principal effectue actuellement une opération fastidieuse, ou il peut y avoir de nombreuses tâches dans la file d'attente des microtâches, c'est peut-être la raison pour laquelle tout le monde a critiqué setTimeout
Ce qui précède n'est que mon point de vue personnel. de la boucle événementielle. Quelques avis et références d'autres excellents articles
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!