Les Web Workers en Javascript

28 février 2021 · 2 min de lecture

Le Javascript est exécuté dans un seul processus (“single threaded” en anglais). Lorsqu’on démarre un long traitement, l’interface utilisateur peut être ralentie voire figée.

sigtrap

Pour pallier à ce problème les Web Workers sont apparus. Ils permettent d’exécuter un script en arrière plan, dans un autre processus.

Cas concret sans Web Worker

Voici un exemple en pur Javascript.

Copiez le code suivant dans un document HTML test.html. Ouvrez ce fichier dans le navigateur.

<span id="info">0</span>
<button onclick="start()">GO</button>

<script>
const start = () => {
  console.log('started')
  console.time('duree')
  
  // simulation d'un long traitement
  for (let i = 0; i <= 5000000; i++) {
    // affichage du compteur
    document.getElementById("info").textContent = i;
  }
  
  console.timeEnd('duree')
}
</script>

L’effet attendu quand on clique sur GO est que l’affichage du compteur progresse durant la boucle. Or, cela ne se produit pas car le processus principal n’a pas la main pour modifier le rendu de la page web. Il n’est mis à jour qu’à la fin du traitement.

web worker

Utilisation d’un Web Worker

Créons un fichier worker.js dans le même dossier que notre page test.html :

self.onmessage = (e) => {
  dojob(e.data.initialCount)
  postMessage({ fini: true })
}

function dojob (initialCount) {
  // simulation d'un long traitement
  for (let i = initialCount; i <= 500000; i++) {
    postMessage({ count: i })
  }
}

A noter que le Web Worker n’a pas accès au DOM car il est exécuté en dehors de la page HTML principale. Ils peuvent communiquer via la transmission de messages. Chaque partie peut envoyer des messages à l’aide de la méthode postMessage() et réagir aux messages reçus grâce au gestionnaire d’évènement onmessage.

Modifions le fichier test.html :

<span id="info">0</span>
<button onclick="start()">GO</button>

<script>
let worker = new Worker('worker.js')
let count = 0

// lorsque le worker enverra un message
worker.onmessage = (e) => {
  // l'instruction de terminer est reçue
  if ('fini' in e.data && e.data.fini) {
    console.log('the end')
    worker.terminate()
    return
  }
  // on met à jour l'affichage du compteur
  document.getElementById("info").textContent = e.data.count;
}

function start () {
  // démarrage du worker
  worker.postMessage({ initialCount: count });
}
</script>

Par sécurité les navigateurs n’autorisent pas l’inclusion d’un fichier javascript depuis le système de fichiers. Le code Worker('worker.js') renverra une erreur. Pour la démonstration, il est donc nécessaire d’utiliser un simple serveur web. Vous avez le choix selon votre language préféré :

En Python :

python3 -m http.server

NodeJS :

npm install -g http-server
http-server -p 8000

PHP:

php -S 127.0.0.1:8000

Allez à l’addresse http://0.0.0.0:8000/test.html dans votre navigateur. Cliquez sur GO. Vous constatez maintenant que le compteur s’affiche en temps réel.

web worker
PARTAGER

A LIRE EGALEMENT