JavaScript e discussioni


Risposte:


109

Vedi http://caniuse.com/#search=worker per le informazioni di supporto più aggiornate.

Quello che segue è stato lo stato di supporto intorno al 2009.


Le parole per cui vuoi cercare Google sono Discussioni dei lavoratori JavaScript

Oltre a Gears non c'è nulla di disponibile in questo momento, ma ci sono molti discorsi su come implementarlo, quindi immagino che guardi questa domanda poiché la risposta cambierà senza dubbio in futuro.

Ecco la documentazione pertinente per Gears: WorkerPool API

WHATWG ha una bozza di raccomandazione per i thread di lavoro: Web Workers

E c'è anche il thread di lavoro DOM di Mozilla


Aggiornamento: giugno 2009, stato attuale del supporto del browser per i thread JavaScript

Firefox 3.5 ha operatori Web. Alcune demo di web worker, se vuoi vederle in azione:

Il plugin Gears può essere installato anche in Firefox.

Safari 4 e i nightly di WebKit hanno thread di lavoro:

Chrome ha Gears integrato, quindi può fare discussioni, anche se richiede una richiesta di conferma da parte dell'utente (e utilizza un'API diversa per gli operatori Web, sebbene funzionerà in qualsiasi browser con il plug-in Gears installato):

  • Demo di Google Gears WorkerPool (non un buon esempio in quanto funziona troppo velocemente per testare in Chrome e Firefox, anche se IE lo esegue abbastanza lentamente da vederlo bloccare l'interazione)

IE8 e IE9 possono eseguire discussioni solo con il plug-in Gears installato


1
Sebbene Safari 4 supporti i web worker, sembra che solo Firefox supporti il ​​passaggio di oggetti complessi tramite postMessage: hacks.mozilla.org/2009/07/working-smarter-not-harder Vedi l'ultimo paragrafo di questo post sull'uso del mondo reale nel progetto Bespin per i collegamenti a uno shim che implementa l'API di lavoro in termini di Google Gears e che aggiunge le funzionalità mancanti all'implementazione di Safari 4 dei lavoratori e i dettagli su come hanno implementato eventi personalizzati trasparenti sull'interfaccia di postMessage.
Sam Hasler,

6
Ora IE9 è uscito, puoi aggiornare "IE8 può fare solo discussioni con il plugin Gears installato" a "IE8 e IE9 può fare solo discussioni con il plugin Gears installato"
BenoitParis

2
@ inf3rno per fare lunghi calcoli su un altro thread in modo che non rallentino l'interfaccia utente del browser.
Sam Hasler,

6
@SamHasler Potresti voler rivedere la tua risposta. Gli operatori Web sono ora supportati da tutti i browser desktop moderni. Vedi anche caniuse.com/#search=worker
Rob W

2
@SamHasler vale anche la pena notare che Google Gears non è più supportato.
skeggse il

73

Modo diverso di fare multi-threading e asincrono in JavaScript

Prima di HTML5 JavaScript consentiva solo l'esecuzione di un thread per pagina.

Ci fosse un modo hacky per simulare un'esecuzione asincrona con Yield , setTimeout(), setInterval(), XMLHttpRequesto gestori di eventi (vedere la fine di questo post per un esempio con resa e setTimeout()).

Ma con HTML5 ora possiamo usare i thread di lavoro per parallelizzare l'esecuzione delle funzioni. Ecco un esempio di utilizzo.


Vero multi-threading

Multi-threading: thread di lavoro JavaScript

HTML5 ha introdotto i thread di lavoro Web (vedi: compatibilità dei browser )
Nota: IE9 e le versioni precedenti non lo supportano.

Questi thread di lavoro sono thread JavaScript eseguiti in background senza influire sulle prestazioni della pagina. Per ulteriori informazioni su Web Worker leggi la documentazione o questa esercitazione .

Ecco un semplice esempio con 3 thread di Web Worker che contano fino a MAX_VALUE e mostrano il valore calcolato corrente nella nostra pagina:

//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>

Possiamo vedere che i tre thread vengono eseguiti in concorrenza e stampano il loro valore corrente nella pagina. Non congelano la pagina perché vengono eseguiti in background con thread separati.


Multi-threading: con più iframe

Un altro modo per raggiungere questo obiettivo è utilizzare più iframe , ognuno eseguirà un thread. Possiamo fornire all'iframe alcuni parametri tramite l'URL e l' iframe può comunicare con il suo genitore per ottenere il risultato e stamparlo (l' iframe deve trovarsi nello stesso dominio).

Questo esempio non funziona in tutti i browser! gli iframe di solito vengono eseguiti nello stesso thread / processo della pagina principale (ma Firefox e Chromium sembrano gestirli in modo diverso).

Poiché lo snippet di codice non supporta più file HTML, fornirò semplicemente i diversi codici qui:

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);
    }
})();

Simula il multi-threading

Single thread: emula la concorrenza JavaScript con setTimeout ()

Il modo 'ingenuo' sarebbe di eseguire la funzione setTimeout()una dopo l'altra in questo modo:

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

Ma questo metodo non funziona perché ogni attività verrà eseguita una dopo l'altra.

Possiamo simulare l'esecuzione asincrona chiamando la funzione in modo ricorsivo in questo modo:

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>

Come puoi vedere, questo secondo metodo è molto lento e congela il browser perché utilizza il thread principale per eseguire le funzioni.


Single thread: emula la concorrenza JavaScript con la resa

Rendimento è una nuova funzionalità di ECMAScript 6 , funziona solo con la versione più vecchia di Firefox e Chrome (in Chrome è necessario abilitare JavaScript sperimentale che appare in chrome: // flags / # enable-javascript-harmony ).

La parola chiave yield rende l'esecuzione della funzione generatore in pausa e il valore dell'espressione che segue la parola chiave yield viene restituito al chiamante del generatore. Può essere pensato come una versione basata sul generatore della parola chiave return.

Un generatore consente di sospendere l'esecuzione di una funzione e riprenderla in un secondo momento. Un generatore può essere utilizzato per programmare le tue funzioni con una tecnica chiamata trampolino .

Ecco l'esempio:

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>


3
Questa dovrebbe davvero essere la risposta migliore.
Jerry Liu,

14

Con le "specifiche laterali" HTML5 non è più necessario hackerare javascript con setTimeout (), setInterval (), ecc.

HTML5 & Friends introduce i Web Worker javascript specifica . È un'API per l'esecuzione di script in modo asincrono e indipendente.

Collegamenti alle specifiche e un tutorial .


11

Non esiste un vero threading in JavaScript. JavaScript è la lingua malleabile che è, ti permette di emularne una parte. Ecco un esempio che mi sono imbattuto l'altro giorno.


1
Cosa intendi con "threading vero"? I fili verdi sono fili veri.
Wes,

10

Non esiste un vero multi-threading in Javascript, ma è possibile ottenere un comportamento asincrono utilizzando setTimeout() e richieste AJAX asincrone.

Cosa stai cercando di realizzare esattamente?


7

Ecco solo un modo per simulare il multi-threading in Javascript

Ora ho intenzione di creare 3 thread che calcoleranno l'aggiunta di numeri, i numeri possono essere divisi con 13 e i numeri possono essere divisi con 3 fino a 10000000000. E queste 3 funzioni non sono in grado di eseguire contemporaneamente al significato di Concurrency. Ma ti mostrerò un trucco che farà funzionare queste funzioni in modo ricorsivo nello stesso tempo: jsFiddle

Questo codice mi appartiene.

Parte del corpo

    <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>

Parte Javascript

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();

Spero che in questo modo sarà utile.


6

Puoi usare Narrative JavaScript , un compilatore che trasformerà il tuo codice in una macchina a stati, permettendoti di emulare il thread in modo efficace. Lo fa aggiungendo un operatore "cedente" (indicato come '->') al linguaggio che consente di scrivere codice asincrono in un singolo blocco di codice lineare.



3

In Javascript non elaborato, il meglio che puoi fare è usare le poche chiamate asincrone (xmlhttprequest), ma non è proprio thread e molto limitato. Google Gears aggiunge una serie di API al browser, alcune delle quali possono essere utilizzate per il supporto dei thread.


1
L'API di Google Gears non è più disponibile.
Ludovic Feltz,

3

Se non puoi o non vuoi usare alcun materiale AJAX, usa un iframe o dieci! ;) Puoi avere processi in esecuzione in iframe in parallelo con la pagina principale senza preoccuparti di problemi comparabili tra browser o problemi di sintassi con dot net AJAX ecc. E puoi chiamare il JavaScript della pagina principale (incluso il JavaScript che ha importato) da un iframe.

Ad esempio, in un iframe principale, per chiamare egFunction()il documento principale una volta caricato il contenuto dell'iframe (questa è la parte asincrona)

parent.egFunction();

Generare dinamicamente anche gli iframe in modo che il codice html principale sia libero da essi, se lo si desidera.


1
Questa descrizione è stata un po 'troppo breve per i miei gusti. Potresti approfondire su come eseguire questa tecnica o pubblicare un link a un tutorial che mostra del codice?
Oligofren,

3

Un altro possibile metodo sta usando un interprete javascript nell'ambiente javascript.

Creando più interpreti e controllandone l'esecuzione dal thread principale, è possibile simulare il multi-threading con ciascun thread in esecuzione nel proprio ambiente.

L'approccio è in qualche modo simile ai web worker, ma dai all'interprete l'accesso all'ambiente globale del browser.

Ho realizzato un piccolo progetto per dimostrarlo .

Una spiegazione più dettagliata in questo post del blog .


1

Javascript non ha discussioni, ma abbiamo dei lavoratori.

I lavoratori possono essere una buona scelta se non hai bisogno di oggetti condivisi.

La maggior parte delle implementazioni del browser in realtà diffonderà i lavoratori su tutti i core, consentendo di utilizzare tutti i core. Puoi vedere una demo di questo qui .

Ho sviluppato una libreria chiamata task.js che lo rende molto facile da fare.

task.js Interfaccia semplificata per l'esecuzione del codice ad alta intensità di CPU su tutti i core (node.js e web)

Un esempio sarebbe

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
});

0

Con la specifica HTML5 non è necessario scrivere troppo JS per lo stesso o trovare alcuni hack.

Una delle funzionalità introdotte in HTML5 è Web Workers che è JavaScript in esecuzione in background, indipendentemente da altri script, senza influire sulle prestazioni della pagina.

È supportato in quasi tutti i browser:

Chrome - 4.0+

IE - 10.0+

Mozilla - 3.5+

Safari - 4.0+

Opera - 11.5+

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.