HTML 5 Web Workers (multi thread)
Un esempio di come è possibile, con le nuove feature di HTML 5 e JavaScript, gestire più thread in un processo separato tramite i Web Workers e farli comunicare con la pagina attraverso dei messaggi.
Vedi anche W3C Web Workers
Allo stato attuale funziona su Internet Explore 10 e sulle ultime versioni di Firefox, Chrome, Opera e Safari
Demo
Nell'esempio propongo un contatore che si incrementa ogni 100 ms e può essere avviato, messo in pausa o fermato. Il conteggio è gestito in un thread separato contenuto nel file worker1.js. E' in un file separato perchè l'oggetto worker vuole un file .js separato dalla pagina che lo esegue. La comunicazione tra la pagina principale e il thread avviene tramite messaggi ed eventi (postMessage e onmessage).HTML 5 Web Worker - Sgart.it
Result: 0
Codice
Per creare un Web Workers la procedura è questa:- si crea l'oggetto Worker passandogli il nome del file (new Worker(this.fileName))
- si aggancia un evento per gestire i messaggi inviati dal thread (this.worker.onmessage)
- nel codice JavaScript del worker si crea la funzione da eseguire (workerExecute) e si inviano i messaggi (postMessage(...))
- se necessario, tramite setTimeout lo si esegue all'infinito
- si gestisce l'evento onmessage per ricevere i messaggi inviati dalla pagina HTML Worker
Attenzione, alcuni browser, per impostazioni di sicurezza, bloccano l'esecuzione dei Worker se eseguiti da un percorso locale file://. Conviene sempre eseguirli tramite un webserver su protocollo http o https
HTML: default.html
<html>
<head>
<title>HTML 5 Web Worker - Sgart.it</title>
</head>
<body>
<h1>HTML 5 Web Worker - Sgart.it</h1>
<div>
Result: <span id="result">0</span>
<div>
<input type="button" value="start" onclick="sgart.worker.start()" />
<input type="button" value="pause" onclick="sgart.worker.pause()" />
<input type="button" value="stop" onclick="sgart.worker.stop()" />
</div>
</div>
<script type="text/javascript">
var sgart = sgart || {};
sgart.worker = {
worker: null,
fileName: "worker1.js",
id: "result",
start: function(){
if(typeof(Worker) === "undefined") {
document.getElementById(this.id).innerHTML = "Worker not supported - upgrade your browser";
} else {
var tmpID = this.id;
// il worker deve essere in un file separato
if(this.worker === null) {
// creo l'oggetto Worker
this.worker = new Worker(this.fileName);
// gestisco l'evento per ricevere i messaggi dal worker
this.worker.onmessage = function(ev){
var msg = ev.data;
document.getElementById(tmpID).innerHTML = msg.counter + " ( " + msg.name + " ) ";
};
} else {
// invio i messaggi al Worker
this.worker.postMessage("start");
}
}
},
pause: function(){
if(this.worker !== null) {
//invio un messaggio al worker
this.worker.postMessage("pause");
}
},
stop: function(){
if(this.worker !== null) {
// termino e de-registro il worker
this.worker.terminate();
this.worker = null;
document.getElementById(this.id).innerHTML = "stop";
}
}
}
</script>
</body>
</html>
JavaScript: worker1.js
// da questo file (il Worker) qui non posso accedere al DOM
var i = 0;
var pause = false;
// funzione da eseguire
function workerExecute()
{
// costruisco l'oggetto da passare come messaggio
// ( posso usare anche valori semplici es: postMessage(i) )
var msg = {counter: ++i, name: "workerExecute"};
// invio il messaggio
postMessage(msg);
// rieseguo la funzione a cadenza regolare
if (pause === true)
pause = false;
else
setTimeout("workerExecute()", 100);
}
// gestisco l'evento che riceve i messaggi dalla pagina
onmessage = function (ev) {
if (ev.data === "pause") {
pause = true;
} else if (ev.data === "start") {
pause = false;
workerExecute();
}
/*if (ev.data === "close") {
var msg = {counter: 0, name: "close"};
postMessage(msg);
self.close();
}*/
};
//eseguo il metodo quando viene istanziato
workerExecute();
Vedi anche W3C Web Workers