JavaScript und Threads

137

Gibt es eine Möglichkeit, Multithreading in JavaScript durchzuführen?

Niyaz
quelle
2
JavaScript enthält zumindest in der aktuellen Form keine Threads. Was genau versuchst du zu tun?
Eric Pohl
3
Können Sie die akzeptierte Antwort auf diese ändern? stackoverflow.com/a/30891727/2576706 Es ist viel weiter entwickelt für zukünftige Benutzer ..
Ludovic Feltz

Antworten:

109

Unter http://caniuse.com/#search=worker finden Sie die aktuellsten Supportinformationen.

Das Folgende war der Stand der Unterstützung um 2009.


Die Wörter, nach denen Sie googeln möchten, sind JavaScript Worker Threads

Abgesehen von Gears ist derzeit nichts verfügbar, aber es wird viel darüber gesprochen, wie dies implementiert werden kann. Ich denke, Sie sollten sich diese Frage ansehen, da sich die Antwort zweifellos in Zukunft ändern wird.

Hier ist die relevante Dokumentation für Gears: WorkerPool API

WHATWG hat einen Empfehlungsentwurf für Worker-Threads: Web Workers

Und es gibt auch Mozillas DOM Worker Threads


Update: Juni 2009, aktueller Stand der Browserunterstützung für JavaScript-Threads

Firefox 3.5 hat Web-Worker. Einige Demos von Web-Workern, wenn Sie sie in Aktion sehen möchten:

Das Gears-Plugin kann auch in Firefox installiert werden.

Safari 4 und die WebKit-Nightlies haben Arbeitsthreads :

In Chrome ist Gears integriert, sodass Threads ausgeführt werden können, obwohl eine Bestätigungsaufforderung des Benutzers erforderlich ist (und eine andere API als bei Web-Workern verwendet wird, obwohl dies in jedem Browser mit installiertem Gears-Plugin funktioniert):

  • Google Gears WorkerPool-Demo (kein gutes Beispiel, da es zu schnell zum Testen in Chrome und Firefox ausgeführt wird, obwohl der IE es langsam genug ausführt, um zu sehen, dass es die Interaktion blockiert)

IE8 und IE9 können nur Threads mit dem installierten Gears-Plugin ausführen

Sam Hasler
quelle
1
Obwohl Safari 4 Web-Worker unterstützt, unterstützt anscheinend nur Firefox das Übergeben komplexer Objekte über postMessage: hacks.mozilla.org/2009/07/working-smarter-not-harder Siehe den letzten Absatz dieses Beitrags zur realen Verwendung im Bespin-Projekt Für Links zu einem Shim, der die Worker-API in Bezug auf Google Gears implementiert und die fehlenden Funktionen zur Worker-Implementierung von Safari 4 hinzufügt, sowie Details zur Implementierung transparenter benutzerdefinierter Ereignisse über der postMessage-Oberfläche.
Sam Hasler
6
Jetzt, da IE9 nicht mehr verfügbar ist, können Sie "IE8 kann nur Threads mit installiertem Gears-Plugin
ausführen
2
@ inf3rno für langwierige Berechnungen an einem anderen Thread, damit die Benutzeroberfläche des Browsers nicht verlangsamt wird.
Sam Hasler
6
@ SamHasler Vielleicht möchten Sie Ihre Antwort überarbeiten. Web-Worker werden jetzt von allen modernen Desktop-Browsern unterstützt. Siehe auch caniuse.com/#search=worker
Rob W
2
@SamHasler Es ist auch erwähnenswert, dass Google Gears nicht mehr unterstützt wird.
skeggse
73

Verschiedene Möglichkeiten, Multithreading und Asynchron in JavaScript durchzuführen

Vor HTML5 erlaubte JavaScript nur die Ausführung eines Threads pro Seite.

Es gab einig hacky Weg , um eine asynchrone Ausführung mit simulieren Yield , setTimeout(), setInterval(), XMLHttpRequestoder Event - Handler (siehe Ende dieses Beitrags für ein Beispiel mit Ertrag und setTimeout()).

Mit HTML5 können wir jetzt Worker-Threads verwenden, um die Ausführung von Funktionen zu parallelisieren. Hier ist ein Anwendungsbeispiel.


Echtes Multithreading

Multithreading: JavaScript Worker-Threads

HTML5 führte Web Worker-Threads ein (siehe: Browserkompatibilität )
Hinweis: IE9 und frühere Versionen unterstützen dies nicht.

Diese Arbeitsthreads sind JavaScript-Threads, die im Hintergrund ausgeführt werden, ohne die Leistung der Seite zu beeinträchtigen. Weitere Informationen zu Web Worker finden Sie in der Dokumentation oder in diesem Lernprogramm .

Hier ist ein einfaches Beispiel mit 3 Web Worker-Threads, die bis MAX_VALUE zählen und den aktuell berechneten Wert auf unserer Seite anzeigen:

//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo){ return window.URL.createObjectURL(new Blob([foo.toString().match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1]],{type:'text/javascript'})); }

var MAX_VALUE = 10000;

/*
 *	Here are the workers
 */
//Worker 1
var worker1 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
//We add a listener to the worker to get the response and show it in the page
worker1.addEventListener('message', function(e) {
  document.getElementById("result1").innerHTML = e.data;
}, false);


//Worker 2
var worker2 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker2.addEventListener('message', function(e) {
  document.getElementById("result2").innerHTML = e.data;
}, false);


//Worker 3
var worker3 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker3.addEventListener('message', function(e) {
    document.getElementById("result3").innerHTML = e.data;
}, false);


// Start and send data to our worker.
worker1.postMessage(MAX_VALUE); 
worker2.postMessage(MAX_VALUE); 
worker3.postMessage(MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

Wir können sehen, dass die drei Threads gleichzeitig ausgeführt werden und ihren aktuellen Wert auf der Seite drucken. Sie frieren die Seite nicht ein, da sie im Hintergrund mit getrennten Threads ausgeführt werden.


Multithreading: mit mehreren Iframes

Eine andere Möglichkeit, dies zu erreichen, besteht darin, mehrere Iframes zu verwenden , von denen jeder einen Thread ausführt. Wir können dem Iframe einige Parameter über die URL geben und der Iframe kann mit seinem Elternteil kommunizieren, um das Ergebnis zu erhalten und es zurückzudrucken (der Iframe muss sich in derselben Domäne befinden).

Dieses Beispiel funktioniert nicht in allen Browsern! iframes werden normalerweise im selben Thread / Prozess wie die Hauptseite ausgeführt (Firefox und Chromium scheinen jedoch unterschiedlich damit umzugehen ).

Da das Code-Snippet nicht mehrere HTML-Dateien unterstützt, werde ich hier nur die verschiedenen Codes bereitstellen:

index.html:

//The 3 iframes containing the code (take the thread id in param)
<iframe id="threadFrame1" src="thread.html?id=1"></iframe>
<iframe id="threadFrame2" src="thread.html?id=2"></iframe>
<iframe id="threadFrame3" src="thread.html?id=3"></iframe>

//Divs that shows the result
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>


<script>
    //This function is called by each iframe
    function threadResult(threadId, result) {
        document.getElementById("result" + threadId).innerHTML = result;
    }
</script>

thread.html:

//Get the parameters in the URL: http://stackoverflow.com/a/1099670/2576706
function getQueryParams(paramName) {
    var qs = document.location.search.split('+').join(' ');
    var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g;
    while (tokens = re.exec(qs)) {
        params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
    }
    return params[paramName];
}

//The thread code (get the id from the URL, we can pass other parameters as needed)
var MAX_VALUE = 100000;
(function thread() {
    var threadId = getQueryParams('id');
    for(var i=0; i<MAX_VALUE; i++){
        parent.threadResult(threadId, i);
    }
})();

Multithreading simulieren

Single-Thread: Emulieren Sie die JavaScript-Parallelität mit setTimeout ()

Der "naive" Weg wäre, die Funktion setTimeout()nacheinander wie folgt auszuführen :

setTimeout(function(){ /* Some tasks */ }, 0);
setTimeout(function(){ /* Some tasks */ }, 0);
[...]

Diese Methode funktioniert jedoch nicht, da jede Aufgabe nacheinander ausgeführt wird.

Wir können die asynchrone Ausführung simulieren, indem wir die Funktion rekursiv wie folgt aufrufen:

var MAX_VALUE = 10000;

function thread1(value, maxValue){
    var me = this;
    document.getElementById("result1").innerHTML = value;
    value++;
  
    //Continue execution
    if(value<=maxValue)
        setTimeout(function () { me.thread1(value, maxValue); }, 0);
}

function thread2(value, maxValue){
    var me = this;
    document.getElementById("result2").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread2(value, maxValue); }, 0);
}

function thread3(value, maxValue){
    var me = this;
    document.getElementById("result3").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread3(value, maxValue); }, 0);
}

thread1(0, MAX_VALUE);
thread2(0, MAX_VALUE);
thread3(0, MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

Wie Sie sehen können, ist diese zweite Methode sehr langsam und friert den Browser ein, da der Hauptthread zum Ausführen der Funktionen verwendet wird.


Single-Thread: Emulieren Sie die JavaScript-Parallelität mit Yield

Die Ausbeute ist ein neues Feature in ECMAScript 6 , es funktioniert nur auf der ältesten Version von Firefox und Chrome (in Chrome müssen Sie aktivieren Experimental JavaScript erscheint in chrome: // flags / # enable-Javascript-Harmonie ).

Das Yield-Schlüsselwort bewirkt, dass die Ausführung der Generatorfunktion angehalten wird und der Wert des Ausdrucks nach dem Yield-Schlüsselwort an den Aufrufer des Generators zurückgegeben wird. Es kann als generatorbasierte Version des Schlüsselworts return betrachtet werden.

Mit einem Generator können Sie die Ausführung einer Funktion unterbrechen und später fortsetzen. Ein Generator kann verwendet werden, um Ihre Funktionen mit einer Technik namens Trampolin zu planen .

Hier ist das Beispiel:

var MAX_VALUE = 10000;

Scheduler = {
	_tasks: [],
	add: function(func){
		this._tasks.push(func);
	},	
	start: function(){
		var tasks = this._tasks;
		var length = tasks.length;
		while(length>0){
			for(var i=0; i<length; i++){
				var res = tasks[i].next();
				if(res.done){
					tasks.splice(i, 1);
					length--;
					i--;
				}
			}
		}
	}	
}


function* updateUI(threadID, maxValue) {
  var value = 0;
  while(value<=maxValue){
	yield document.getElementById("result" + threadID).innerHTML = value;
	value++;
  }
}

Scheduler.add(updateUI(1, MAX_VALUE));
Scheduler.add(updateUI(2, MAX_VALUE));
Scheduler.add(updateUI(3, MAX_VALUE));

Scheduler.start()
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

Ludovic Feltz
quelle
3
Dies sollte wirklich die beste Antwort sein.
Jerry Liu
14

Mit den HTML5 "Side-Specs" müssen Sie kein Javascript mehr mit setTimeout (), setInterval () usw. hacken .

HTML5 & Friends führt die Javascript Web Workers- Spezifikation ein. Es ist eine API zum asynchronen und unabhängigen Ausführen von Skripten.

Links zur Spezifikation und ein Tutorial .

djondal
quelle
11

In JavaScript gibt es kein echtes Threading. Da JavaScript die formbare Sprache ist, können Sie einige davon emulieren. Hier ist ein Beispiel, das mir neulich begegnet ist.

Svend
quelle
1
Was meinst du mit "True Threading"? Grüne Fäden sind echte Fäden.
Wes
10

In Javascript gibt es kein echtes Multithreading, aber Sie können asynchrones Verhalten mit setTimeout()und asynchronen AJAX-Anforderungen erzielen.

Was genau versuchst du zu erreichen?

17 von 26
quelle
7

Hier ist nur eine Möglichkeit, Multithreading in Javascript zu simulieren

Jetzt werde ich 3 Threads erstellen, die die Addition von Zahlen berechnen, Zahlen können mit 13 geteilt werden und Zahlen können mit 3 bis 10000000000 geteilt werden. Und diese 3 Funktionen können nicht gleichzeitig ausgeführt werden, was Parallelität bedeutet. Aber ich werde Ihnen einen Trick zeigen, mit dem diese Funktionen gleichzeitig rekursiv ausgeführt werden: jsFiddle

Dieser Code gehört mir.

Körperteil

    <div class="div1">
    <input type="button" value="start/stop" onclick="_thread1.control ? _thread1.stop() : _thread1.start();" /><span>Counting summation of numbers till 10000000000</span> = <span id="1">0</span>
</div>
<div class="div2">
    <input type="button" value="start/stop" onclick="_thread2.control ? _thread2.stop() : _thread2.start();" /><span>Counting numbers can be divided with 13 till 10000000000</span> = <span id="2">0</span>
</div>
<div class="div3">
    <input type="button" value="start/stop" onclick="_thread3.control ? _thread3.stop() : _thread3.start();" /><span>Counting numbers can be divided with 3 till 10000000000</span> = <span id="3">0</span>
</div>

Javascript Teil

var _thread1 = {//This is my thread as object
    control: false,//this is my control that will be used for start stop
    value: 0, //stores my result
    current: 0, //stores current number
    func: function () {   //this is my func that will run
        if (this.control) {      // checking for control to run
            if (this.current < 10000000000) {
                this.value += this.current;   
                document.getElementById("1").innerHTML = this.value;
                this.current++;
            }
        }
        setTimeout(function () {  // And here is the trick! setTimeout is a king that will help us simulate threading in javascript
            _thread1.func();    //You cannot use this.func() just try to call with your object name
        }, 0);
    },
    start: function () {
        this.control = true;   //start function
    },
    stop: function () {
        this.control = false;    //stop function
    },
    init: function () {
        setTimeout(function () {
            _thread1.func();    // the first call of our thread
        }, 0)
    }
};
var _thread2 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 13 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("2").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread2.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread2.func();
        }, 0)
    }
};
var _thread3 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 3 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("3").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread3.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread3.func();
        }, 0)
    }
};

_thread1.init();
_thread2.init();
_thread3.init();

Ich hoffe dieser Weg wird hilfreich sein.

Ahmet Can Güven
quelle
6

Sie können Narrative JavaScript verwenden , einen Compiler, der Ihren Code in eine Zustandsmaschine umwandelt und so das Threading effektiv emuliert. Dazu wird der Sprache ein "nachgebender" Operator (mit '->' bezeichnet) hinzugefügt, mit dem Sie asynchronen Code in einen einzelnen linearen Codeblock schreiben können.

Antoine Aubry
quelle
4

Die neue v8-Engine, die heute herauskommen soll, unterstützt sie (glaube ich)

Juan
quelle
3

In rohem Javascript können Sie am besten nur die wenigen asynchronen Aufrufe (xmlhttprequest) verwenden, aber das ist kein wirkliches Threading und sehr eingeschränkt. Google Gears fügt dem Browser eine Reihe von APIs hinzu, von denen einige für die Threading-Unterstützung verwendet werden können.

jsight
quelle
1
Die Google Gears-API ist nicht mehr verfügbar.
Ludovic Feltz
3

Wenn Sie kein AJAX-Material verwenden können oder wollen, verwenden Sie einen oder zehn Iframes! ;) Sie können Prozesse in Iframes parallel zur Masterseite ausführen lassen, ohne sich über browserübergreifende vergleichbare Probleme oder Syntaxprobleme mit dot net AJAX usw. Gedanken machen zu müssen, und Sie können das JavaScript der Masterseite (einschließlich des von ihr importierten JavaScript) von einem aufrufen iframe.

Beispiel: Rufen Sie in einem übergeordneten Iframe egFunction()das übergeordnete Dokument auf, sobald der Iframe-Inhalt geladen wurde (das ist der asynchrone Teil).

parent.egFunction();

Generieren Sie die Iframes auch dynamisch, damit der Haupt-HTML-Code frei von ihnen ist, wenn Sie möchten.

Jono
quelle
1
Diese Beschreibung war etwas zu kurz für meinen Geschmack. Könnten Sie entweder erläutern, wie diese Technik ausgeführt wird, oder einen Link zu einem Tutorial veröffentlichen, das Code enthält?
Oligofren
3

Eine andere mögliche Methode ist die Verwendung eines Javascript-Interpreters in der Javascript-Umgebung.

Indem Sie mehrere Interpreter erstellen und deren Ausführung über den Hauptthread steuern, können Sie Multithreading simulieren, wobei jeder Thread in einer eigenen Umgebung ausgeführt wird.

Der Ansatz ähnelt dem von Web-Workern, Sie geben dem Interpreter jedoch Zugriff auf die globale Umgebung des Browsers.

Ich habe ein kleines Projekt gemacht, um dies zu demonstrieren .

Eine ausführlichere Erklärung in diesem Blogbeitrag .

Amitay Dobo
quelle
1

Javascript hat keine Threads, aber wir haben Arbeiter.

Arbeiter sind möglicherweise eine gute Wahl, wenn Sie keine gemeinsam genutzten Objekte benötigen.

Die meisten Browser-Implementierungen verteilen die Worker tatsächlich auf alle Kerne, sodass Sie alle Kerne verwenden können. Eine Demo dazu finden Sie hier .

Ich habe eine Bibliothek namens task.js entwickelt , die dies sehr einfach macht.

task.js Vereinfachte Schnittstelle, damit CPU-intensiver Code auf allen Kernen (node.js und Web) ausgeführt werden kann.

Ein Beispiel wäre

function blocking (exampleArgument) {
    // block thread
}

// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);

// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
    // do something with result
});
Chad Scira
quelle
0

Mit der HTML5- Spezifikation müssen Sie nicht zu viel JS für dasselbe schreiben oder einige Hacks finden.

Eine der in HTML5 eingeführten Funktionen ist Web Workers, bei dem JavaScript unabhängig von anderen Skripten im Hintergrund ausgeführt wird, ohne die Leistung der Seite zu beeinträchtigen.

Es wird in fast allen Browsern unterstützt:

Chrome - 4.0+

IE - 10.0+

Mozilla - 3.5+

Safari - 4.0+

Opera - 11.5+

Mandeep Singh
quelle