it-swarm.com.de

Ist es garantiert, dass JavaScript Single-Threaded ist?

Es ist bekannt, dass JavaScript in allen modernen Browser-Implementierungen Single-Thread-fähig ist. Ist dies jedoch in einem Standard festgelegt oder ist dies nur aus Tradition? Ist es absolut sicher anzunehmen, dass JavaScript immer Single-Threaded ist?

581
Egor Pavlikhin

Das ist eine gute Frage. Ich würde gerne "Ja" sagen. Ich kann nicht.

Bei JavaScript wird in der Regel davon ausgegangen, dass ein einzelner Ausführungsthread für Skripte (*) sichtbar ist. Wenn Sie also ein Inline-Skript, einen Ereignis-Listener oder eine Zeitüberschreitung eingeben, behalten Sie die vollständige Kontrolle, bis Sie das Ende Ihres Blocks oder Ihrer Funktion erreicht haben.

(*: Ignoriert die Frage, ob Browser ihre JS-Engines wirklich mit einem OS-Thread implementieren oder ob andere eingeschränkte Ausführungsthreads von WebWorkern eingeführt werden.)

In Wirklichkeit ist dies jedoch ist nicht ganz richtig, auf hinterhältige böse Art und Weise.

Der häufigste Fall sind unmittelbare Ereignisse. Browser werden diese sofort auslösen, wenn Ihr Code etwas unternimmt, um sie zu verursachen:

var l= document.getElementById('log');
var i= document.getElementById('inp');
i.onblur= function() {
    l.value+= 'blur\n';
};
setTimeout(function() {
    l.value+= 'log in\n';
    l.focus();
    l.value+= 'log out\n';
}, 100);
i.focus();
<textarea id="log" rows="20" cols="40"></textarea>
<input id="inp">

Ergebnisse in log in, blur, log out Auf allen außer IE. Diese Ereignisse werden nicht nur ausgelöst, weil Sie focus() direkt aufgerufen haben, sondern auch, weil Sie alert() aufgerufen haben oder ein Popup-Fenster oder etwas anderes geöffnet haben, das den Fokus verschiebt.

Dies kann auch zu anderen Ereignissen führen. Fügen Sie beispielsweise einen Listener i.onchange Hinzu und geben Sie etwas in die Eingabe ein, bevor der Aufruf von focus() den Fokus aufhebt, und die Protokollreihenfolge ist log in, change, blur, log out, Außer in Opera wo es ist log in, blur, log out, change und IE wo es ist (noch weniger erklärbar) log in, change, log out, blur.

Wenn Sie click() für ein Element aufrufen, das es bereitstellt, wird der onclick -Handler sofort in allen Browsern aufgerufen (zumindest ist dies konsistent!).

(Ich verwende hier die direkten on... - Ereignishandlereigenschaften, aber das gleiche passiert mit addEventListener und attachEvent.)

Es gibt auch eine Reihe von Umständen, unter denen Ereignisse ausgelöst werden können, während Ihr Code eingebunden ist, obwohl Sie nichts getan haben, um ihn zu provozieren. Ein Beispiel:

var l= document.getElementById('log');
document.getElementById('act').onclick= function() {
    l.value+= 'alert in\n';
    alert('alert!');
    l.value+= 'alert out\n';
};
window.onresize= function() {
    l.value+= 'resize\n';
};
<textarea id="log" rows="20" cols="40"></textarea>
<button id="act">alert</button>

Drücken Sie alert und Sie erhalten ein modales Dialogfeld. Es wird kein Skript mehr ausgeführt, bis Sie diesen Dialog schließen, ja? Nee. Wenn Sie die Größe des Hauptfensters ändern, wird im Textbereich alert in, resize, alert out Angezeigt.

Möglicherweise können Sie die Größe eines Fensters nicht ändern, während ein modales Dialogfeld angezeigt wird. Unter Linux können Sie die Größe des Fensters beliebig ändern. Unter Windows ist dies nicht so einfach. Sie können jedoch die Bildschirmauflösung von einer größeren in eine kleinere ändern, bei der das Fenster nicht passt. Dadurch wird die Größe des Fensters geändert.

Sie könnten denken, dass nur resize (und wahrscheinlich ein paar mehr wie scroll) ausgelöst werden können, wenn der Benutzer keine aktive Interaktion mit dem Browser hat, weil das Skript als Thread ausgeführt wird. Und für einzelne Fenster könnten Sie recht haben. Aber das ist alles, sobald Sie Cross-Window-Scripting durchführen. Bei allen Browsern außer Safari, bei denen alle Fenster/Tabs/Frames blockiert werden, wenn einer von ihnen belegt ist, können Sie über den Code eines anderen Dokuments mit einem Dokument interagieren, das in einem separaten Ausführungsthread ausgeführt wird und dazu führt, dass verwandte Ereignishandler ausgeführt werden Feuer.

Stellen, an denen Ereignisse, die Sie generieren lassen können, ausgelöst werden können, während das Skript noch weiterentwickelt ist:

  • wenn die modalen Popups (alert, confirm, Prompt) in allen Browsern außer Opera geöffnet sind;

  • während showModalDialog in Browsern, die dies unterstützen;

  • das Dialogfeld "Ein Skript auf dieser Seite ist möglicherweise ausgelastet ..." lässt Ereignisse wie "Größe ändern" und "Unschärfe" auch dann zu, wenn das Skript gerade ausgeführt wird Busy-Loop, außer in Opera.

  • vor einiger Zeit konnte es mir in IE mit dem Sun Java Plugin möglich sein, durch Aufrufen einer beliebigen Methode in einem Applet Ereignisse auszulösen und Skripte erneut einzugeben Dies war immer ein zeitabhängiger Fehler, und es ist möglich, dass Sun ihn seitdem behoben hat (das hoffe ich natürlich).

  • wahrscheinlich mehr. Es ist schon eine Weile her, dass ich das getestet habe und die Browser haben seitdem an Komplexität gewonnen.

Zusammenfassend scheint JavaScript für die meisten Benutzer die meiste Zeit über einen strengen ereignisgesteuerten Einzelthread zu verfügen. In Wirklichkeit hat es so etwas nicht. Es ist nicht klar, wie viel davon einfach ein Fehler ist und wie viel bewusstes Design, aber wenn Sie komplexe Anwendungen schreiben, insbesondere fensterübergreifende/Frame-Scripting-Anwendungen, gibt es jede Chance, dass es Sie beißen könnte - und in Abständen, schwer zu debuggende Möglichkeiten.

Im schlimmsten Fall können Sie Parallelitätsprobleme lösen, indem Sie alle Ereignisantworten umleiten. Wenn ein Ereignis eintrifft, legen Sie es in einer Warteschlange ab und bearbeiten Sie die Warteschlange später in einer setInterval -Funktion. Wenn Sie ein Framework schreiben, das von komplexen Anwendungen verwendet werden soll, ist dies möglicherweise ein guter Schritt. postMessage wird hoffentlich auch in Zukunft die Schmerzen beim Cross-Document-Scripting lindern.

558
bobince

Ich würde ja sagen, weil praktisch der gesamte (zumindest nicht triviale) JavaScript-Code kaputt gehen würde, wenn die JavaScript-Engine eines Browsers sie asynchron ausführen würde.

Hinzu kommt, dass HTML5 spezifiziert bereits Web Worker (eine explizite, standardisierte API für Multithreading-Javascript-Code) das Einführen von Multithreading in das grundlegende Javascript zumeist sinnlos wäre.

( Hinweis für andere Kommentatoren: Obwohl setTimeout/setInterval, HTTP-Request-Onload-Ereignisse (XHR) und UI-Ereignisse (Klicken, Fokussieren usw.) vermitteln einen groben Eindruck von Multithreading - sie werden immer noch alle auf einer einzelnen Zeitachse ausgeführt - also auch dann, wenn Wir kennen ihre Ausführungsreihenfolge nicht im Voraus. Es besteht kein Grund zur Sorge, dass sich die externen Bedingungen während der Ausführung eines Ereignishandlers, einer zeitgesteuerten Funktion oder eines XHR-Rückrufs ändern.)

112
Már Örlygsson

Ja, obwohl bei der Verwendung von asynchronen APIs wie setInterval- und xmlhttp-Callbacks einige Probleme bei der gleichzeitigen Programmierung (hauptsächlich Race-Bedingungen) auftreten können.

16
spender

Ja, obwohl Internet Explorer 9 Ihr Javascript in Vorbereitung auf die Ausführung im Hauptthread in einem separaten Thread kompiliert. Für Sie als Programmierer ändert dies jedoch nichts.

10
ChessWhiz

JavaScript/ECMAScript wurde entwickelt, um in einer Host-Umgebung zu leben. Dies bedeutet, dass JavaScript nicht wirklich nichts tun ist, es sei denn, die Host-Umgebung entscheidet, ein bestimmtes Skript zu analysieren und auszuführen und Umgebungsobjekte bereitzustellen, die JavaScript tatsächlich nützlich machen (wie das DOM in Browsern).

Ich denke, eine bestimmte Funktion oder ein bestimmter Skriptblock wird Zeile für Zeile ausgeführt, was für JavaScript garantiert ist. Möglicherweise kann eine Host-Umgebung jedoch mehrere Skripts gleichzeitig ausführen. Oder eine Host-Umgebung kann immer ein Objekt bereitstellen, das Multithreading bietet. setTimeout und setInterval sind Beispiele oder zumindest Pseudobeispiele einer Host-Umgebung, die eine Möglichkeit bieten, eine gewisse Parallelität durchzuführen (auch wenn es sich nicht genau um eine Parallelität handelt).

7
Bob

Tatsächlich kann ein übergeordnetes Fenster mit untergeordneten Fenstern oder untergeordneten Fenstern oder Frames kommunizieren, für die eigene Ausführungsthreads ausgeführt werden.

7
kennebec

Ich würde sagen, dass die Spezifikation jemanden nicht daran hindert, eine Engine zu erstellen, die Javascript auf mehreren Threads ausführt, und den Code zum Ausführen der Synchronisierung für den Zugriff auf den Status des gemeinsam genutzten Objekts benötigt.

Ich denke, das nicht blockierende Paradigma mit nur einem Thread ist darauf zurückzuführen, dass JavaScript in Browsern ausgeführt werden muss, in denen die Benutzeroberfläche niemals blockieren sollte.

Nodejs ist dem Ansatz der Browser gefolgt.

Rhino unterstützt jedoch das Ausführen von js-Code in verschiedenen Threads . Die Ausführungen können den Kontext nicht gemeinsam nutzen, sie können jedoch den Bereich gemeinsam nutzen. Für diesen speziellen Fall heißt es in der Dokumentation:

... "Rhino garantiert, dass Zugriffe auf Eigenschaften von JavaScript - Objekten über Threads hinweg atomar sind, übernimmt jedoch keine weiteren Garantien für Skripte, die zur gleichen Zeit im gleichen Bereich ausgeführt werden. Wenn zwei Skripte gleichzeitig den gleichen Bereich verwenden, Die Skripte sind für die Koordination aller Zugriffe auf gemeinsam genutzte Variablen verantwortlich . "

Aus dem Lesen der Rhino-Dokumentation schließe ich, dass es für jemanden möglich sein kann, eine Javascript-API zu schreiben, die auch neue JavaScript-Threads erzeugt, die API jedoch rhino-spezifisch ist (z. B. kann der Knoten nur einen neuen Prozess erzeugen).

Ich stelle mir vor, dass selbst für eine Engine, die mehrere Threads in Javascript unterstützt, Kompatibilität mit Skripten bestehen sollte, die kein Multithreading oder Blocking in Betracht ziehen.

Browser und NodeJs so zu lernen, wie ich es sehe:

  • Wird der gesamte js-Code in einem einzigen Thread ausgeführt? : Ja.

  • Kann js-Code dazu führen, dass andere Threads ausgeführt werden? : Ja.

  • Können diese Threads den Ausführungskontext von j verändern ?: Nein. Sie können jedoch (direkt/indirekt (?)) An die Ereigniswarteschlange angehängt werden.

Bei Browsern und NodeJs (und wahrscheinlich vielen anderen Engines) ist Javascript also nicht multithreaded, sondern die Engines selbst.

6
Marinos An

@ Bobince liefert eine wirklich undurchsichtige Antwort.

Nach der Antwort von Már Örlygsson ist Javascript immer einfach aufgebaut, weil alles in Javascript auf einer einzigen Zeitachse ausgeführt wird.

Das ist die strikte Definition einer Singlethread-Programmiersprache.

6
williamle8300

Nein.

Ich gehe hier gegen die Menge, aber ertrage es mit mir. Ein einzelnes JS-Skript soll effektiv ein Thread sein, dies bedeutet jedoch nicht, dass es nicht unterschiedlich interpretiert werden kann.

Angenommen, Sie haben den folgenden Code ...

var list = [];
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

Dies wird mit der Erwartung geschrieben, dass die Liste am Ende der Schleife 10000 Einträge haben muss, die das Quadrat des Index darstellen, aber die VM könnte feststellen, dass sich jede Iteration der Schleife nicht auf die andere auswirkt, und sie mit neu interpretieren zwei Fäden.

Erster Thread

for (var i = 0; i < 5000; i++) {
  list[i] = i * i;
}

Zweiter Thread

for (var i = 5000; i < 10000; i++) {
  list[i] = i * i;
}

Ich vereinfache hier, weil JS-Arrays komplizierter sind als dumme Speicherblöcke. Wenn diese beiden Skripte jedoch in der Lage sind, dem Array thread-sichere Einträge hinzuzufügen, müssen beide Arrays nach Abschluss der Ausführung ausgeführt werden das gleiche Ergebnis wie die Singlethread-Version.

Mir ist zwar nicht bekannt, dass VM parallelisierbaren Code wie diesen erkennt, es ist jedoch wahrscheinlich, dass er in Zukunft für JIT-VMs ​​verfügbar sein wird, da er in einigen Situationen eine höhere Geschwindigkeit bieten kann.

Wenn Sie dieses Konzept weiterentwickeln, ist es möglich, dass Code mit Anmerkungen versehen wird, damit die VM weiß, was in Multithread-Code konvertiert werden soll.

// like "use strict" this enables certain features on compatible VMs.
"use parallel";

var list = [];

// This string, which has no effect on incompatible VMs, enables threading on
// this loop.
"parallel for";
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

Da Web-Worker zu Javascript kommen, ist es unwahrscheinlich, dass dieses ... hässlichere System jemals existieren wird, aber ich denke, es ist sicher zu sagen, dass Javascript traditionell Single-Threaded ist.

4
max

Nun, Chrome ist ein Multiprozess, und ich denke, jeder Prozess befasst sich mit seinem eigenen Javascript-Code, aber soweit der Code weiß, handelt es sich um "Single-Threaded".

Es gibt keinerlei Unterstützung in Javascript für Multithreading, zumindest nicht explizit, so dass es keinen Unterschied macht.

3
Francisco Soto

Ich habe das Beispiel von @ bobince mit geringfügigen Änderungen ausprobiert:

<html>
<head>
    <title>Test</title>
</head>
<body>
    <textarea id="log" rows="20" cols="40"></textarea>
    <br />
    <button id="act">Run</button>
    <script type="text/javascript">
        let l= document.getElementById('log');
        let b = document.getElementById('act');
        let s = 0;

        b.addEventListener('click', function() {
            l.value += 'click begin\n';

            s = 10;
            let s2 = s;

            alert('alert!');

            s = s + s2;

            l.value += 'click end\n';
            l.value += `result = ${s}, should be ${s2 + s2}\n`;
            l.value += '----------\n';
        });

        window.addEventListener('resize', function() {
            if (s === 10) {
                s = 5;
            }

            l.value+= 'resize\n';
        });
    </script>
</body>
</html>

Wenn Sie also auf "Ausführen" klicken, das Warnungs-Popup schließen und einen "einzelnen Thread" erstellen, sollte Folgendes angezeigt werden:

click begin
click end
result = 20, should be 20

Wenn Sie jedoch versuchen, dies in Opera oder Firefox stable unter Windows auszuführen und das Fenster mit dem angezeigten Warnungs-Popup zu minimieren/maximieren, wird es ungefähr so ​​aussehen:

click begin
resize
click end
result = 15, should be 20

Ich möchte nicht sagen, dass dies "Multithreading" ist, aber ein Teil des Codes wurde in einer falschen Zeit ausgeführt, wobei ich dies nicht erwartet habe und jetzt einen beschädigten Zustand habe. Und besser über dieses Verhalten Bescheid wissen.

2