Heim  >  Artikel  >  Web-Frontend  >  Kann JavaScript Multithreading sein?

Kann JavaScript Multithreading sein?

高洛峰
高洛峰Original
2016-11-26 10:16:57924Durchsuche

JavaScripts setTimeout und setInterval sind zwei Methoden, die die Gefühle anderer Leute leicht täuschen können, weil wir oft denken, dass sie beim Aufruf auf eine vorgegebene Weise ausgeführt werden, wie zum Beispiel


[javascript]
setTimeout( function(){ Alert('Hello!'); } , 0);
setTimeout( function(){ Alert ('Hallo!'); } , 0);
setInterval( callbackFunction , 100);

Ich denke, die Begrüßungsmethode in setTimeout wird sofort ausgeführt, da dies nicht ausgesagt wird dünn, aber das JavaScript-API-Dokument definiert die Bedeutung des zweiten Parameters eindeutig als die Anzahl der Millisekunden, nach denen die Rückrufmethode ausgeführt wird. Wenn er auf 0 Millisekunden gesetzt ist, wird er natürlich sofort ausgeführt , die callbackFunction-Methode von setInterval wird jedes Mal ausgeführt. Ich habe keinen Zweifel daran, dass sie sofort nach einem Intervall von 100 Millisekunden ausgeführt wird!

Aber wenn Ihre Erfahrung in der Entwicklung von JavaScript-Anwendungen weiter zunimmt und bereichert, werden Sie eines Tages Finden Sie einen seltsamen Code und Sie sind verwirrt:

[javascript]

div.onclick = function(){
setTimeout( function(){document .getElementById('inputField').focus();}, 0 );
div.onclick = function(){
setTimeout( function(){document.getElementById('inputField') .focus();}, 0);
};


Da es nach 0 Millisekunden ausgeführt wird, warum sollte man setTimeout in diesem Moment verwenden? >

Bis zum letzten Tag haben Sie versehentlich einen fehlerhaften Absatz geschrieben. Code:


[javascript]

setTimeout( function(){ while(true){} } , 100); 🎜>setTimeout( function(){ Alert('Hello!' ); } , 200);

setInterval( callbackFunction , 200);

setTimeout( function(){ while(true){} } , 100) ;
setTimeout( function(){ alarm('you OK!'); } , 200);
setInterval( callbackFunction , 200);



Die erste Zeile von Code tritt in eine Endlosschleife ein, aber Sie werden bald feststellen, dass die zweite und dritte Zeile nicht Ihren Erwartungen entspricht, die Alarmbegrüßung nicht angezeigt wurde und es keine Neuigkeiten von callbacKFunction gab.

Zu diesem Zeitpunkt sind Sie es Diese Situation ist völlig inakzeptabel, denn der Prozess des Erkennens und Akzeptierens neuer Ideen ist schmerzhaft, aber die Suche nach der Wahrheit von JavaScript wird vor Schmerzen nicht aufhören Wir beginnen die Reise der Erkundung von JavaScript-Threads und Timern! Die JavaScript-Engine verfügt über mehrere Threads, die ausgeführt werden, und die JavaScript-Timer-Rückruffunktion ist eine asynchrone Ausführung.

Tatsächlich verwendet JavaScript die meiste Zeit eine Blendungsmethode, um unsere Augen zu täuschen. Die Hintergrundbeleuchtung hier muss die Tatsache verdeutlichen:

Die JavaScript-Engine läuft in einem einzigen Thread, der Browser hat immer nur einen Thread, der das JavaScript-Programm ausführt.

Es ist auch sinnvoll, dass die JavaScript-Engine in einem einzigen Thread läuft Ein einzelner Thread muss sich nicht um komplexe Probleme wie die Thread-Synchronisierung kümmern, und das Problem wird vereinfacht.

Wie arbeitet die Single-Thread-JavaScript-Engine also mit dem Browser-Kernel zusammen, um diese Timer zu verarbeiten und auf Browser-Ereignisse zu reagieren? ?

Das Folgende ist eine kurze Erklärung basierend auf der Browser-Kernel-Verarbeitungsmethode.

Durchsuchen Die Browser-Kernel-Implementierung ermöglicht die asynchrone Ausführung dieser Threads unter Kernel-Steuerung, um die Synchronisierung aufrechtzuerhalten Eine Browser-Kernel-Implementierung verfügt über mindestens drei residente Threads: JavaScript-Engine-Thread, Schnittstellen-Rendering-Thread und Browser-Ereignisauslösung. Zusätzlich zu Threads gibt es auch einige Threads, die nach der Ausführung beendet werden, z. B. HTTP-Anforderungsthreads asynchrone Ereignisse. Das folgende Diagramm veranschaulicht, wie die Single-Threaded-JavaScript-Engine mit anderen Threads interagiert und kommuniziert. Obwohl die Implementierungsdetails jedes Browserkernels unterschiedlich sind, sind die Aufrufprinzipien ähnlich >JavaScripts setTimeout und setInterval sind zwei Methoden, die die Gefühle anderer leicht täuschen können, weil Wir denken oft, dass es auf die etablierte Weise ausgeführt wird, zum Beispiel

Wie aus dem Bild ersichtlich ist, ist die JavaScript-Engine ereignisgesteuert. Die Ereignisse können hier als verschiedene Aufgaben angesehen werden, die ihr vom Browser zugewiesen werden stammen aus dem Codeblock, der aktuell von der JavaScript-Engine ausgeführt wird, z. B. dem Aufruf von setTimeout zum Hinzufügen einer Aufgabe, oder aus dem Browser. Andere Threads des Kernels, z. B. Mausklickereignisse von Schnittstellenelementen, Benachrichtigungen zur geplanten Triggerzeit und asynchrone Änderung des Anforderungsstatus Benachrichtigungen usw. Aus Code-Sicht sind Aufgabenentitäten verschiedene Rückruffunktionen, und die JavaScript-Engine wartet auf das Eintreffen von Aufgaben in der Aufgabenwarteschlange. Aufgrund der Single-Thread-Beziehung müssen diese Aufgaben in die Warteschlange gestellt und verarbeitet werden durch den Motor nacheinander.


Die obige Abbildung t1-t2..tn stellt verschiedene Zeitpunkte dar, und die entsprechenden kleinen Quadrate unter tn stellen die Zeitpunkte dar. Vorausgesetzt, es ist der Zeitpunkt t1, ist der Motor Lassen Sie uns zu diesem Zeitpunkt den Status anderer Threads im Browserkernel beschreiben.

T1-Zeit:

GUI-Rendering-Thread:

Dieser Thread ist für das Rendern der HTML-Elemente der Browseroberfläche verantwortlich oder wenn durch einen Vorgang ein Reflow verursacht wird. Dieser Thread konzentriert sich jedoch auf die Erläuterung JavaScript-Timing-Mechanismus: Zu diesem Zeitpunkt muss über den Rendering-Thread gesprochen werden, da dieser Thread sich gegenseitig mit dem JavaScript-Engine-Thread ausschließt. Dies ist leicht zu verstehen, da JavaScript-Skripte DOM-Elemente manipulieren können, wenn sie die Eigenschaften dieser Elemente ändern Beim gleichzeitigen Rendern der Schnittstelle sind die vor und nach dem Rendering-Thread erhaltenen Elementdaten möglicherweise inkonsistent.

Während die JavaScript-Engine das Skript ausführt, befindet sich der Browser-Rendering-Thread in einem angehaltenen Zustand. Das bedeutet, dass es „eingefroren“ ist.

Führen Sie es also im Skript aus. Aktualisierungen der Schnittstelle, wie das Hinzufügen von Knoten, das Löschen von Knoten oder das Ändern des Erscheinungsbilds von Knoten, werden nicht sofort widergespiegelt werden in einer Warteschlange gespeichert und können gerendert werden, wenn die JavaScript-Engine inaktiv ist.

GUI-Ereignis-auslösender Thread:

Die Ausführung des JavaScript-Skripts hat keinen Einfluss auf die Auslösung des HTML-Elements Im Zeitraum t1 klickt der Benutzer zunächst auf eine Maustaste und der Klick wird durch den Browser-Ereignisthread ausgelöst. Nach der Erfassung wird ein Mausklickereignis für den JavaScript-Engine-Thread gebildet. Dieses Ereignis wird von anderen Threads asynchron an das Ende der Aufgabenwarteschlange übertragen. Da die Engine die Aufgabe bei t1 verarbeitet, wartet dieses Mausklickereignis auf die Verarbeitung

Timing-Trigger-Thread:

Beachten Sie, dass der Timing-Zähler des Browsermodells hier nicht von der JavaScript-Engine gezählt wird, da die JavaScript-Engine Single-Threaded ist und nicht zählen kann, wenn sie sich in einem blockierten Thread-Zustand befindet. Sie muss sich auf die Zeit und das Trigger-Timing von außen verlassen. Daher sind die geplanten Ereignisse in der Warteschlange auch asynchrone Ereignisse.

Wie aus der Abbildung hervorgeht, ist in diesem t1-Zeitraum nach dem Auslösen des Mausklickereignisses auch das zuvor festgelegte setTimeout Das Timing ist angekommen. In diesem Moment generiert der Timing-Trigger-Thread ein asynchrones Timing-Ereignis und stellt es in die Aufgabenwarteschlange. Das Ereignis wird nach dem Klickereignis-Rückruf in die Warteschlange gestellt und wartet auf die Verarbeitung Während des Zeitraums wurde auch ein bestimmter setInterval-Timer hinzugefügt, der während des t1-Zeitraums zweimal nacheinander ausgelöst wurde. Diese beiden Ereignisse wurden zur Verarbeitung in die Warteschlange gestellt > Es ist ersichtlich, dass, wenn der Zeitraum t1 sehr lang ist, viel größer als das Zeitintervall von setInterval, der Timing-Trigger-Thread kontinuierlich asynchrone Timing-Ereignisse generiert und diese an das Ende der Aufgabenwarteschlange stellt, unabhängig davon, ob dies der Fall ist oder nicht verarbeitet, aber sobald t1 und die ersten Aufgaben vor dem geplanten Ereignis verarbeitet wurden, werden die geplanten Ereignisse in diesen Anordnungen nacheinander ohne Unterbrechung ausgeführt. Dies liegt daran, dass für die JavaScript-Engine jede Aufgabe in der Die Verarbeitungswarteschlange wird auf die gleiche Weise verarbeitet.

Nach t1, d. h. wenn die aktuell verarbeitete Aufgabe zurückgegeben wurde, überprüft die JavaScript-Engine die Aufgabenwarteschlange und stellt fest, dass die aktuelle Wenn die Warteschlange nicht leer ist, wird die entsprechende Aufgabe unter t2 zur Ausführung herausgenommen, und so weiter. Aus dieser Sicht:

Wenn die Warteschlange nicht leer ist, nimmt die Engine eine heraus Aufgabe vom Kopf der Warteschlange bis zur Verarbeitung der Aufgabe, dh nach der Rückkehr führt die Engine die nächste Aufgabe aus. Bevor die Aufgabe zurückkehrt, wird die Engine andere Aufgaben nicht ausführen.

Ich glaube Sie haben jetzt ein klares Verständnis dafür, ob JavaScript mehrere Threads ausführen kann, und verstehen auch den JavaScript-Timer-Betriebsmechanismus. Lassen Sie uns einige Fälle analysieren:

Fall 1: setTimeout und setInterval

[javascript]

setTimeout(function(){

/* Codeblock... */
setTimeout(arguments.callee, 10);
}, 10);

setInterval(function(){
/*Code block... */
}, 10);
setTimeout(function( ){
/* Code block... */
setTimeout(arguments.callee, 10);
}, 10);

setInterval(function(){
/ *Code block... */

}, 10);



Diese beiden Codeteile sehen zusammen gleich aus, sind es aber nicht. Das setTimeout in der Callback-Funktion im ersten Absatz wird nach der Ausführung der JavaScript-Engine gesetzt dass es ein Zeitintervall vom Abschluss der vorherigen Rückrufverarbeitung bis zum Beginn der nächsten Rückrufverarbeitung gibt. Theoretisch beträgt das Ausführungszeitintervall zwischen zwei setTimeout-Rückrufen. Der zweite Abschnitt beginnt, nachdem setInterval das Timing festgelegt hat. Der zeitgesteuerte Trigger-Thread generiert kontinuierlich alle zehn Sekunden asynchrone zeitgesteuerte Ereignisse und stellt sie an das Ende der Aufgabenwarteschlange. Theoretisch beträgt das Ausführungszeitintervall zwischen zwei setInterval-Rückrufen Fall 2: Ajax asynchron Anfrage Ist es wirklich asynchron?

Viele Klassenkameraden und Freunde sind verwirrt. Da JavaScript angeblich in einem einzelnen Thread ausgeführt wird, ist XMLHttpRequest nach der Verbindung wirklich asynchron?

Tatsächlich ist die Anfrage tatsächlich asynchron, aber Diese Anfrage Der Browser öffnet eine neue Thread-Anfrage (siehe Abbildung oben). Wenn zuvor ein Rückruf festgelegt wurde, generiert der asynchrone Thread ein Statusänderungsereignis und stellt es in die Verarbeitungswarteschlange der JavaScript-Engine Wenn die Aufgabe verarbeitet wird, führt die JavaScript-Engine die Rückruffunktion immer in einem einzelnen Thread aus, insbesondere wird die von onreadystatechange festgelegte Funktion in einem einzelnen Thread ausgeführt

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