Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erläuterung von Beispielen für die js-Ereignisschleife

Detaillierte Erläuterung von Beispielen für die js-Ereignisschleife

零下一度
零下一度Original
2017-06-26 09:17:381217Durchsuche

Ich habe schon einige Event-Loop-Blogs gelesen, aber nachdem ich sie eine Weile nicht gelesen hatte, wurde mir klar, dass ich sie alle vergessen hatte, also beschloss ich, einen Blog zu schreiben, um sie zusammenzufassen!

Erklären wir zunächst, was die Ereignisschleife ist:

Soweit wir wissen, ist das Js des Browsers Single-Threaded, d. h Beispielsweise wird höchstens ein Codesegment gleichzeitig ausgeführt, aber der Browser kann asynchrone Anfragen sehr gut verarbeiten. Warum also? Schauen wir uns zunächst ein Bild an
Detaillierte Erläuterung von Beispielen für die js-Ereignisschleife
Auf dem obigen Bild können wir sehen, dass der js-Hauptthread einen Ausführungsstapel hat und der gesamte js-Code im Ausführungsstapel ausgeführt wird. Wenn Sie während der Ausführung des Codes auf asynchronen Code stoßen (z. B. setTimeout, Ajax, Promise.then und Benutzerklicks usw.), fügt der Browser diese Codes in einen Thread ein (hier nennen wir ihn „Behind-the“) -Szenen-Thread). Durch Warten wird die Ausführung des Hauptthreads nicht blockiert Wenn auf die Anfrage geantwortet wird, wird sie vom Thread verarbeitet. Die Rückruffunktion wird in die Aufgabenwarteschlange gestellt und wartet auf die Ausführung. Wenn der Hauptthread mit der Ausführung des gesamten Codes im Stapel fertig ist, prüft er, ob eine auszuführende Aufgabe in der Aufgabenwarteschlange vorhanden ist. Wenn eine auszuführende Aufgabe vorhanden ist, wird die Aufgabe gestellt zur Ausführung in den Ausführungsstapel. Wenn die aktuelle Aufgabenwarteschlange leer ist, wartet sie in einer Schleife weiter auf das Eintreffen der Aufgabe. Daher wird dies als Ereignisschleife bezeichnet.

Hier kommt also die Frage. Welche Aufgabe sollte zuerst ausgeführt werden, wenn sich viele Aufgaben in der Aufgabenwarteschlange befinden?

Tatsächlich (wie im Bild oben gezeigt) verfügt js über zwei Aufgabenwarteschlangen, eine heißt Makrotask-Warteschlange (Aufgabenwarteschlange) und die andere heißt Mikrotask-Warteschlange

  • Ersteres wird hauptsächlich für relativ umfangreiche Arbeiten verwendet. Zu den häufigsten gehören setTimeout, setInterval, Benutzerinteraktionsvorgänge, UI-Rendering usw.

  • Letzteres wird hauptsächlich für relativ kleine Arbeiten verwendet, was häufig vorkommt. Es gibt Promise, process.nextTick (nodejs)

Was sind also die spezifischen Unterschiede zwischen den beiden? , wenn zwei Aufgaben gleichzeitig erscheinen, welche sollte ausgewählt werden?
Tatsächlich macht die Ereignisschleife Folgendes:

  1. Überprüfen Sie, ob die Makrotask-Warteschlange leer ist . Wenn nicht, fahren Sie mit dem nächsten Schritt fort, springen Sie zu 3

  2. Nehmen Sie die erste Aufgabe aus der Makrotask-Warteschlange (mit der längsten Zeit in der Warteschlange) und führen Sie sie aus im Ausführungsstapel (nur einer). Fahren Sie nach der Ausführung mit dem nächsten Schritt fort

  3. Überprüfen Sie, ob die Microtask-Warteschlange leer ist. Wenn nicht, fahren Sie mit dem nächsten Schritt fort, andernfalls springen Sie zu 1 (eine neue Ereignisschleife starten)

  4. Aus der Mikrotask-Warteschlange abrufen Die Aufgabe an der Spitze der Warteschlange (mit der längsten Zeit in der Warteschlange) wird zur Ausführung in die Ereigniswarteschlange gestellt. Springt nach der Ausführung zu 3

Unter anderem wird die während der Ausführung des Codes hinzugefügte Mikrotask-Aufgabe im aktuellen Ereignisschleifenzeitraum ausgeführt und die neu hinzugefügte Eine Makrotask-Aufgabe kann nur warten, bis die nächste Ereignisschleife ausgeführt wird (eine Ereignisschleife führt nur eine Makrotask aus).
Schauen wir uns zunächst einen Code an.

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)

Was sollte Ihrer Meinung nach das Ergebnis sein? ?
Die Ergebnisse, die ich in der Knotenumgebung und der Chrome-Konsole ausgebe, sind wie folgt:

1
9
7
8
2
3
10
11
12
13

Im obigen Beispiel
die erste Ereignisschleife:

  1. console.log(1) wird ausgeführt, Ausgabe 1

  2. settimeout1 wird ausgeführt und zur Makrotask-Warteschlange hinzugefügt

  3. setinterval1 wird ausgeführt und zur Makrotask-Warteschlange hinzugefügt

  4. settimeout2 wird ausgeführt und zur Makrotask-Warteschlange hinzugefügt

  5. promise2 wird ausgeführt und seine zwei Die Funktion then wird zur Mikrotask-Warteschlange hinzugefügt

  6. console.log(9) wird ausgeführt und die Ausgabe ist 9

  7. Gemäß Die Definition der Ereignisschleife wird als nächstes ausgeführt. Die hinzugefügten Mikrotask-Aufgaben werden in der Reihenfolge ihres Eintritts in die Warteschlange ausgeführt. Console.log(7) und console.log(8) werden ausgegeben Die Warteschlange ist leer. Gehen Sie zurück zum ersten Schritt und betreten Sie die nächste Ereignisschleife. Zu diesem Zeitpunkt lautet die Makrotask-Warteschlange: settimeout1, setinterval1, settimeout2

Die zweite Ereignisschleife :

  1. aus der Makrotask-Warteschlange Holen Sie sich die Aufgabe an der Spitze des Teams (settimeout1) und führen Sie sie aus.

    Die Mikrotask-Warteschlange ist leer Der erste Schritt und die nächste Ereignisschleife. Zu diesem Zeitpunkt lautet die Makrotask-Warteschlange: setinterval1, settimeout2

Die dritte Ereignisschleife:

  1. Nehmen Sie die Aufgabe an der Spitze des Teams (setinterval1) aus der Makrotask-Warteschlange und führen Sie sie aus, geben Sie 3 aus und fügen Sie dann das neu generierte setinterval1 zur Makrotask-Warteschlange hinzu

    Die Mikrotask-Warteschlange ist leer. Kehren Sie zum ersten Schritt zurück und betreten Sie die nächste Ereignisschleife. Zu diesem Zeitpunkt lautet die Makrotask-Warteschlange: settimeout2, setinterval1

Die vierte Ereignisschleife:

  1. Nehmen Sie die Aufgabe an der Spitze des Teams (settimeout2) aus der Makrotask-Warteschlange, führen Sie sie aus, geben Sie 10 aus und führen Sie die Funktion in New Promise aus (Die Funktion in New Promise ist eine synchrone Operation. keine asynchrone Operation), geben Sie 11 aus und fügen Sie die beiden Then-Funktionen zur Mikrotask-Warteschlange hinzu

  2. Nehmen Sie aus der Mikrotask-Warteschlange die Aufgabe an der Spitze der Warteschlange und führen Sie sie aus, bis sie leer ist. Daher werden die beiden neu hinzugefügten Mikrotask-Aufgaben nacheinander ausgeführt, 12 und 13 ausgegeben und setinterval1 gelöscht.
    Zu diesem Zeitpunkt sind sowohl die Mikrotask-Warteschlange als auch die Makrotask-Warteschlange leer, und der Browser prüft immer, ob die Warteschlange leer ist leer und auf neue warten Die Aufgabe wird zur Warteschlange hinzugefügt.

Hier fragen Sie sich vielleicht, warum in der ersten Schleife die Makroaufgabe nicht zuerst ausgeführt wird? Denn sollten wir je nach Prozess nicht zuerst prüfen, ob die Makrotask-Warteschlange leer ist, und dann die Mikrotask-Warteschlange überprüfen?
Grund: Da es sich bei der Aufgabe, die zu Beginn im js-Hauptthread ausgeführt wird, um eine Makrotask-Aufgabe handelt, wird gemäß dem Prozess der Ereignisschleife nur eine Makrotask-Aufgabe in einer Ereignisschleife ausgeführt Im Hauptthread wird die erste Aufgabe aus der Mikrotask-Warteschlange entnommen und ausgeführt.

Hinweis:

Wenn eine Mikrotask-Aufgabe ausgeführt wird, tritt sie nur dann in die nächste Ereignisschleife ein, wenn die Mikrotask-Warteschlange leer ist Mikrotask-Aufgaben führen dazu, dass der Hauptthread Mikrotask-Aufgaben ausführt, und es gibt keine Möglichkeit, Makrotask-Aufgaben auszuführen. Auf diese Weise können wir keine UI-Rendering-/IO-Operationen/Ajax-Anfragen ausführen . geschehen. In „process.nexttick“ in nodejs können Sie die maximale Anzahl von Aufrufen festlegen, um ein Blockieren des Hauptthreads zu verhindern.

Damit führen wir ein neues Problem ein, das Timer-Problem. Ist der Timer echt und zuverlässig? Wenn ich beispielsweise einen Befehl ausführe: setTimeout(task, 100), wird er dann genau nach 100 Millisekunden ausgeführt? Tatsächlich können wir aufgrund der obigen Diskussion wissen, dass dies unmöglich ist.
Ich denke, jeder sollte den Grund kennen, denn nachdem Sie setTimeout(task,100) ausgeführt haben, stellen Sie eigentlich nur sicher, dass die Aufgabe nach 100 Millisekunden in die Makrotask-Warteschlange gelangt, aber das bedeutet vielleicht nicht, dass sie sofort ausgeführt werden kann Der Hauptthread führt derzeit einen zeitaufwändigen Vorgang aus, oder es befinden sich möglicherweise viele Aufgaben in der Mikrotask-Warteschlange. Dies kann der Grund sein, warum alle setTimeout kritisiert haben.

Das Obige ist nur meine persönliche Meinung der Event-Schleife. Einige Meinungen und Referenzen aus anderen hervorragenden Artikeln

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung von Beispielen für die js-Ereignisschleife. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn