Heim  >  Artikel  >  Web-Frontend  >  Ein detaillierter Blick auf die Browser-Ereignisschleife (Codebeispiele)

Ein detaillierter Blick auf die Browser-Ereignisschleife (Codebeispiele)

不言
不言nach vorne
2018-11-12 17:06:451837Durchsuche

Dieser Artikel vermittelt Ihnen ein tiefgreifendes Verständnis der Browser-Ereignisschleife (Codebeispiele). Ich hoffe, dass er Ihnen als Referenz dienen wird.

Die Ereignisschleife des Browsers ist etwas, mit dem das Frontend nur allzu vertraut ist und mit dem wir täglich in Berührung kommen. Aber ich habe es mir immer auswendig gelernt: Die Ereignisaufgabenwarteschlange ist in Makrotask und Mikrotask unterteilt. Der Browser nimmt zuerst eine Aufgabe aus der Makrotask zur Ausführung, führt dann alle Aufgaben in der Mikrotask aus und wechselt dann zur Makrotask eine Aufgabe zur Ausführung herausnehmen... ., und dieser Zyklus geht weiter. Aber für den folgenden Code war ich verwirrt. Gemäß den oben genannten Regeln sollte setTimeout zuerst herausgenommen und ausgeführt werden, aber das Ausführungsergebnis hat mich umgehauen.

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

Nachdem ich die Einführung anderer Leute in Aufgabenwarteschlangen sorgfältig gelesen hatte, wurde mir klar, dass der synchron ausgeführte js-Code tatsächlich eine Makrotask ist (genauer gesagt ist der Code in jedem Skript-Tag eine Makrotask), also das Obige In den Regeln heißt es, dass zuerst eine Makroaufgabe herausnimmt und ausführt.
Viele Artikel im Internet erklären es wie oben. Ich habe immer gedacht, dass dies die HTML-Spezifikation der Ereignisschleife ist, also sollten wir uns daran erinnern. Erst als ich kürzlich den Artikel von Herrn Li Yincheng las (siehe Referenzlink am Ende des Artikels), wurde mir plötzlich klar, dass die Artikel, die ich zuvor gelesen hatte, das Multithreading-Modell des Browsers nicht klar analysierten Aus der Perspektive des Multithreading-Modells des Browsers gehen wir davon aus, dass die Ereignisschleife des Browsers basierend auf der obigen Konvention tatsächlich das Ergebnis des Multithreading-Modells des Browsers ist.

Die Essenz von Makrotask

Makrotask ist im Wesentlichen eine Nachrichtenwarteschlange für die Kommunikation zwischen mehreren Threads des Browsers
In Chrome entspricht jede Seite einem Prozess, der verfügt über mehrere Threads, wie z. B. JS-Thread, Rendering-Thread, IO-Thread, Netzwerk-Thread, Timer-Thread usw. Die Kommunikation zwischen diesen Threads wird durch Hinzufügen einer Aufgabe (PostTask) zur Aufgabenwarteschlange der anderen Partei realisiert.

Die verschiedenen Threads des Browsers laufen in einer for-Endlosschleife. Der Thread selbst oder andere Threads können über die Task-Warteschlange Nachrichten senden Aufgaben, und diese Threads nehmen kontinuierlich Aufgaben aus ihren eigenen Aufgabenwarteschlangen zur Ausführung, oder sie bleiben stehen, bis die festgelegte Zeit abgelaufen ist oder jemand sie bei PostTask aufweckt.

Es kann einfach verstanden werden, dass jeder Thread des Browsers ständig Aufgaben aus seiner eigenen Aufgabenwarteschlange herausnimmt, diese ausführt, die Aufgaben erneut herausnimmt und erneut ausführt, und dies in einer Endlosschleife fortgesetzt wird.

Nehmen Sie den folgenden Code als Beispiel:

<script>
    console.log(1)
    setTimeout(() => {
        console.log(2)
    }, 1000)
    console.log(3)
</script>
  1. Zuerst wird der Code im Skript-Tag als Aufgabe in die Aufgabenwarteschlange des JS-Threads gestellt js-Thread wird aktiviert und dann Nehmen Sie die Aufgabe heraus und führen Sie sie aus

  2. Führen Sie zuerst console.log(1) aus, führen Sie dann setTimeout aus, fügen Sie eine Aufgabe zum Timer-Thread hinzu und führen Sie dann die Konsole aus .log(3). Zu diesem Zeitpunkt ist die Aufgabenwarteschlange des JS-Threads leer und der JS-Thread geht in den Ruhezustand

  3. Ungefähr 1000 ms später fügt der Timer-Thread eine geplante Aufgabe hinzu ( Timer-Rückruf) in die Aufgabenwarteschlange des JS-Threads, und der JS-Thread wird erneut aktiviert. Führen Sie die geplante Rückruffunktion aus und führen Sie schließlich console.log (2) aus.

Wie Sie sehen, bedeutet die sogenannte Makrotask nicht, dass der Browser definiert, welche Aufgaben Makrotask sind. Jeder Thread des Browsers zirkuliert einfach seine eigene Aufgabenwarteschlange führt es kontinuierlich aus. Es ist nur eine Aufgabe.

Microtask

Im Vergleich zu Macrotask, das eine „Illusion“ ist, die durch das Multithreading-Modell des Browsers verursacht wird, handelt es sich bei Microtask um eine Warteschlange, die zum aktuellen Thread gehört, nicht zu anderen Threads PostTask. Die Aufgabe wird nur verzögert (genauer gesagt, sie wird nach dem aktuell ausgeführten Synchronisationscode ausgeführt), wie z. B. Promise.then und MutationObserver.

Nehmen Sie den folgenden Code als Beispiel:

<script>
    new Promise((resolve) => {
       resolve()
       console.log(1)
       setTimeout(() => {
         console.log(2)
       },0)
    }).then(() => {
        console.log(3)
    })
    // 输出:1 3 2
</script>
  1. Zuerst wird der Code im Skript-Tag als Aufgabe in die Aufgabenwarteschlange des JS-Threads gestellt js-Thread wird aktiviert, und dann wird die Aufgabe herausgenommen,

  2. ausgeführt und dann ein neues Promise ausgeführt und in Promise aufgelöst. Nach der Auflösung wird die Rückruffunktion von Promise als Aufgabe verwendet Das muss verzögert und in alle aktuell ausgeführten Synchronisierungen eingefügt werden. Führen Sie nach dem Code

  3. setTimeout aus und fügen Sie eine Aufgabe zum Timer-Thread hinzu

  4. Zu diesem Zeitpunkt wird der Synchronisierungscode ausgeführt und anschließend die verzögerte Ausführung. Die Aufgabe, die dann die Callback-Funktion von Promise ist, besteht darin, console.log(3)

  5. Schließlich ist die Aufgabenwarteschlange des JS-Threads leer und der JS-Thread geht in den Ruhezustand. Nach etwa 1000 ms fügt der Timer-Thread eine geplante Aufgabe (Timer-Rückruf) zur JS-Thread-Aufgabenwarteschlange hinzu und der JS-Thread wird wieder aktiviert und führt die geplante Rückruffunktion aus, nämlich 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
*/


Das obige ist der detaillierte Inhalt vonEin detaillierter Blick auf die Browser-Ereignisschleife (Codebeispiele). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen