back to top

WebWorkers HTML5

Quando ragioniamo in termini di concorrenza la prima cosa che salta alla mente è la parola Thread, per alcuni il termine è associato a Java ed in particolare ad un esame universitario non particolarmente semplice da superare. Da oggi è possibile collegare a "concorrenza" un altro termine il cui campo di applicazione è il mondo client: i WebWorkers.

Si tratta di APIs che permettono di eseguire script (tipicamente lunghe elaborazioni) in background senza che la loro esecuzione blocchi in alcun modo l’interfaccia utente. Il draft dedicato, che potete trovare qui, mette in guardia dal loro abuso e ne consiglia un utilizzo limitato e responsabile.

Ogni WebWorker deve essere inserito in un file, il nome del quale va passato come parametro al costruttore:

var webWorker = new Worker("conteggio.js");

Il meccanismo alla base dei WebWorkers è semplice; viene istanziato l’oggetto indicando il file dove vive la business logic (che rappresenta il worker vero e proprio), si definisce un listener per l’evento message dalle due parti e si scambiano i dati attraverso il metodo postMessage(). Vediamo un esempio:

File principale

// #`Istanzio l'oggetto
var webWorker = new Worker("pariodispari.js");

// # Listener per l'evento message
webWorker.addEventListener("message", function(event) {
  alert(event.data);
}, false);

// # Invio dei dati
webWorker.postMessage({"par": "3"});

pariodispari.js

// # Il WebWorker, self rappresenta il worker stesso
self.addEventListener("message", function(event) {
  
  // # Il messaggio" è nella bottiglia
  var dati = event.data;
  
  // # Il numero è nel messaggio
  var numero = dati.par;
  
  // # Notifichiamo il chiamante del risultato della elaborazione
  if(numero % 2 == 0) {
  self.postMessage("è un numero pari");
  } else {
  self.postMessage("è un numero dispari");
  }
}, false);

È possibile per un worker "delegare" il lavoro a dei subworkers. Lo scopo è spalmare il lavoro su vari threads per sfruttare più CPU e migliorare le performances. Si tratta in buona sostanza di istanziare n workers figli all’interno di un worker padre:

// # Il worker padre, lo smistatore 

// # 10 lavoranti
num_subworker = 10;

for (var i = 0; i < num_subworker; i++) {
  // # la business logic risiede in subworker.js
  var worker = new Worker("subworker.js");
  worker.postMessage(.. la tua parte di lavoro ..);
  ...
}

È anche possibile importare file esterni in un worker tramite importScripts(filename).

L’utilizzo forse più logico di un WebWorker prevede la gestione di chiamate asincrone via get o post; l’esempio che segue implementa il recupero di informazioni attraverso una chiamata asincrona verso un sito (potrebbe essere un servizio REST che ritorna un qualche tipo di risposta attesa dall’applicazione):

//#`Istanzio l'oggetto
var webWorker = new Worker("call.js");

// # Listener per l'evento message
webWorker.addEventListener("message", function(event) {
  alert(event.data);
}, false);

// # Invio dei dati
webWorker.postMessage({ url: "http://www.ilmiosito.it" });

call.js

self.addEventListener("message", function(event) {
  
  try {
    // # Utilizzo qui il buon vecchio XMLHttpRequest
    var xhr = new XMLHttpRequest();
    xhr.open("GET", event.data.url, false);
    xhr.send();
    // # Ritorno lo stato
    self.postMessage(xhr.statusText); 
  } catch (e) {
    self.postMessage("ERRORE");
  }
}, false);

Esistono anche SharedWorkers: utilizzano APIs leggermente diverse e gestiscono connessioni multiple. Vediamo un semplice esempio riportato nel draft:

File principale

var worker = new SharedWorker("shared.js");
worker.port.addEventListener('message', function(event) {
  alert (e.data);
}, false);
worker.port.start();
worker.port.postMessage("ping");

shared.js

// # notate il listener onconnect
onconnect = function(event) {
  var port = e.ports[0];
  port.postMessage("Ciao!");
  port.onmessage = function(event) {
    port.postMessage("pong");
  }
}

Importante: i WebWorkers non hanno accesso a tutti gli oggetti solitamente disponibili, proprio in ragione della lora natura: non possono accedere al DOM ad esempio perchè non è thread-safe, così come agli oggetti window e document, hanno tuttavia accesso a location, navigator e come abbiamo visto ad XMLHttpRequest.

Pubblicità
Articolo precedente
Articolo successivo

In questa guida...