Maison  >  Article  >  interface Web  >  Un examen approfondi de la boucle d'événements du navigateur (exemples de code)

Un examen approfondi de la boucle d'événements du navigateur (exemples de code)

不言
不言avant
2018-11-12 17:06:451835parcourir

Ce que cet article vous apporte, c'est une compréhension approfondie de la boucle d'événements du navigateur (exemples de code). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

La boucle d'événements du navigateur est quelque chose que le front-end connaît très bien et auquel il est exposé quotidiennement. Mais j'ai toujours mémorisé par cœur : La file d'attente des tâches événementielles est divisée en macrotâche et microtâche. Le navigateur retire d'abord une tâche de la macrotâche pour l'exécuter, puis exécute toutes les tâches de la microtâche, puis passe à la macrotâche. pour retirer une tâche à exécuter... ., et ce cycle continue. Mais pour le code suivant, j'ai été confus. SetTimeout appartient à la macrotâche. Selon les règles ci-dessus, setTimeout doit être retiré et exécuté en premier, mais j'ai été giflé par le résultat de l'exécution.

<script>
    setTimeout(() => {
        console.log(1)
    }, 0)
    new Promise((resolve) => {
        console.log(2)
        resolve()
    }).then(() => {
        console.log(3)
    })
    // 我曾经的预期是:2 1 3
    // 实际输出:2 3 1
</script>

Après avoir lu attentivement l'introduction d'autres personnes sur les files d'attente de tâches, j'ai réalisé que le code js exécuté de manière synchrone est en fait une macrotâche (pour être précis, le code dans chaque balise de script est une macrotâche), donc comme mentionné dans les règles ci-dessus, il n'y a aucun problème à supprimer d'abord une macrotâche et exécuter .
De nombreux articles sur Internet l'expliquent comme ci-dessus. J'ai toujours pensé qu'il s'agissait de la spécification HTML de la boucle d'événement, nous devrions donc nous en souvenir. Ce n'est que lorsque j'ai lu récemment l'article de M. Li Yincheng (voir le lien de référence à la fin de l'article) que j'ai soudain réalisé que les articles que j'avais lus auparavant n'analysaient pas clairement le modèle multithread du navigateur depuis le Du point de vue du modèle multithread du navigateur, nous pensons donc que la boucle d'événements du navigateur est basée sur la convention ci-dessus, c'est en fait le résultat du modèle multithread du navigateur.

L'essence de la macrotâche

La macrotâche est essentiellement une file d'attente de messages pour la communication entre plusieurs threads du navigateur
Dans Chrome, chaque page correspond à un processus, qui a plusieurs threads, tels que le thread js, le thread de rendu, le thread io, le thread réseau, le thread de minuterie, etc. La communication entre ces threads se fait en ajoutant une tâche (PostTask) à la file d'attente des tâches de l'autre partie.

Les différents threads du navigateur sont des threads résidents. Ils s'exécutent dans une boucle infinie. Chaque thread a ses propres files d'attente de tâches. Le thread lui-même ou d'autres threads peuvent leur envoyer des messages via PostTask. tâches, et ces threads retireront continuellement les tâches de leurs propres files d'attente de tâches pour les exécuter, ou ils dormiront jusqu'à l'heure définie ou que quelqu'un les réveillera lors de PostTask.

On peut simplement comprendre que chaque thread du navigateur retire constamment des tâches de sa propre file d'attente de tâches, les exécute, retire à nouveau les tâches et les exécute à nouveau, et cela continue dans une boucle infinie.

Prenons le code suivant comme exemple :

<script>
    console.log(1)
    setTimeout(() => {
        console.log(2)
    }, 1000)
    console.log(3)
</script>
  1. Tout d'abord, le code dans la balise script est placé dans la file d'attente des tâches du fil js en tant que tâche , et le thread js est réveillé, puis supprimez la tâche et exécutez

  2. Exécutez d'abord console.log(1), puis exécutez setTimeout, ajoutez une tâche au thread du minuteur, puis exécutez console.log(3). À ce moment, la file d'attente des tâches du thread js est vide et le thread js se met en veille

  3. Environ 1000 ms plus tard, le thread du minuteur ajoute un programmé. tâche (rappel du minuteur) dans la file d'attente des tâches du thread js, et le thread js revient Après avoir été réveillé, la fonction de rappel planifiée est exécutée et enfin console.log(2) est exécuté.

Comme vous pouvez le voir, la soi-disant macrotâche ne signifie pas que le navigateur définit quelles tâches sont des macrotâches. Chaque thread du navigateur fait simplement circuler fidèlement sa propre file d'attente de tâches et. l'exécute en continu. C'est juste une tâche.

microtâche

Par rapport à la macrotâche, qui est une "illusion" provoquée par le modèle multi-thread du navigateur, la microtâche est une file d'attente qui existe. La microtâche appartient au thread actuel, pas aux autres threads. PostTask. La tâche est juste retardée (pour être précis, elle est exécutée après le code de synchronisation actuellement exécuté), comme Promise.then et MutationObserver.

Prenons le code suivant comme exemple :

<script>
    new Promise((resolve) => {
       resolve()
       console.log(1)
       setTimeout(() => {
         console.log(2)
       },0)
    }).then(() => {
        console.log(3)
    })
    // 输出:1 3 2
</script>
  1. Tout d'abord, le code dans la balise script est placé dans la file d'attente des tâches du fil js en tant que tâche , et le thread js est réveillé, puis supprimez la tâche et exécutez

  2. puis exécutez new Promise et résolvez dans Promise. Après la résolution, la fonction de rappel then de promise sera placée dans. la tâche en cours d'exécution comme une tâche qui doit être retardée. Après tout le code de synchronisation

  3. , exécutez setTimeout et ajoutez une tâche au fil du minuteur

  4. .

    À ce moment, le code de synchronisation est exécuté, puis l'exécution est retardée. La tâche exécutée, qui est alors la fonction de rappel de la promesse, consiste à exécuter console.log(3)

  5. Enfin, la file d'attente des tâches du thread js est vide et le thread js se met en veille pendant environ 1000 ms. Enfin, le thread du minuteur ajoute une tâche planifiée (rappel du minuteur) à la file d'attente des tâches du thread js, et le thread js est. se réveille à nouveau et exécute la fonction de rappel planifiée, à savoir console.log(2).

总结

通过上面的分析,可以看到,文章开头提到的规则:浏览器先从macrotask取出一个任务执行,再执行microtask内的所有任务,接着又去macrotask取出一个任务执行...,并没有说错,但这只是浏览器执行机制造成的现象,而不是说浏览器按照这样的规则去执行的代码。
这篇文章中的所有干货都来自李银成大佬的文章,我只是按照自己的理解,做了简化描述,方便大家理解,也加深自己的印象。
最后,看了这篇文章,大家能够基于浏览器的运行机制,分析出下面代码的执行结果了吗(ps:不要用死记硬背的规则去分析哟)

console.log('start')

const interval = setInterval(() => {  
  console.log('setInterval')
}, 0)

setTimeout(() => {  
  console.log('setTimeout 1')
  Promise.resolve()
      .then(() => {
        console.log('promise 3')
      })
      .then(() => {
        console.log('promise 4')
      })
      .then(() => {
        setTimeout(() => {
          console.log('setTimeout 2')
          Promise.resolve()
              .then(() => {
                console.log('promise 5')
              })
              .then(() => {
                console.log('promise 6')
              })
              .then(() => {
                clearInterval(interval)
              })
        }, 0)
      })
}, 0)

Promise.resolve()
    .then(() => {  
        console.log('promise 1')
    })
    .then(() => {
        console.log('promise 2')
    })
// 执行结果
/*  start
    promise 1
    promise 2
    setInterval
    setTimeout 1
    promise 3
    promise 4
    setInterval
    setTimeout 2
    promise 5
    promise 6
*/


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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer