javascript tutorial : multithreading Web Worker en Javascript et html5




 

 
Tel qu’est connu Javascript aujourd’hui, et même si des solutions de contournement existent, ce langage possède une grosse limitation : tout ce qui est exécuté par Javascript l’est dans un seul et même processus. Ce processus étant en charge de toute la partie interface, lorsque Javascript exécute une fonction un peu trop longue, le navigateur va se bloquer le temps que l’exécution se termine et vous n’avez plus le moyen d’agir sur votre navigateur.

Le cas échéant, les navigateurs récents avertissent l’utilisateur et proposent d’arrêter le processus. Aujourd’hui html5 donne au développeur d’écrire des applications multiprocessus grâce aux Web Workers auxquels je vous propose de vous initier dans cet article.

 

Prérequis

– Savoir ce qu’est Javascript;
– en avoir fait un peu;
– un navigateur supportant les Web Workers (Google Chrome);
– de préférence un serveur apache fonctionnel sur lequel déployer les fichiers exemples.

 

Les solutions de contournement au monothreading

La technologie Ajax (Asynchronous Javascript and XML) permet de faire des requêtes http de manière asynchrone et en retour mettre à jour votre page html : dans ce cas, il n’y a pas de blocage du navigateur.

 

Web Workers, c’est quoi ?

Un Web Worker permet d’exécuter du code Javascript en dehors du processus de la page qui l’héberge ou le lance. De ce fait, la page hôte ne se trouve plus bloquer par un traitement et l’utilisateur peut continuer d’interagir avec elle.

Le principe du multithreading consiste pouvoir exécuter en parallèle plusieurs programmes, chacun d’eux constituant son propre fil d’exécution indépendamment des autres. Un Web Worker permet de lancer un fil d’exécution.

Vous pouvez lancer autant de Web Worker que vous voulez, la seule limite étant les ressources disponibles de votre machine.

Un Web Worker est lié à la page html5 qui le lance: il peut communiquer qu’avec sa page hôte. On appelle cela aussi un Web Worker dédié.

 

Mon navigateur supporte t-il les Web Workers ?

Le Web Worker étant une spécification html5, tous les navigateurs ne les supportent pas. Vous pouvez donc vérifier par le biais de Javascript si un navigateur supporte ou pas les Web Workers comme ceci :

if( !!window.Worker ) {
  // web workers supportés
} else {
  // web workers non supportés
}

Je vous invite à consulter l’article Listez les fonctionnalités html5 supportées par votre navigateur pour connaître les spécifications html5 supportées par votre navigateur.

 

Web Workers, le DOM et les autres

Les Web Workers ont chacun leur propre contexte d’exécution indépendamment de celui de la page html5 qui les lance.
Les Web Workers n’ont pas accès au DOM : il est impossible d’utiliser les objets Window ou Document, de même que les cookies.
Les Web Workers :
– peuvent faire des requêtes Ajax;
– ont accès aux méthodes setTimeout/clearTimeout, setInterval/clearInterval;
– peuvent importer d’autres scripts Javascript via la méthode importScripts;
– peuvent s’abonner/désabonner à des évènements.

Si vous tentez d’utiliser le DOM, une exception ReferenceError sera levée.

 

Comment fonctionnent les Web Workers ?

Un Web Worker html5 nécessite 2 entités qui communiquent pour fonctionner :
– la page html5 qui lance le Web Worker;
– le script javascript exécuté par le Web Worker.

La communication entre ces 2 entités se fait par le biais de messages, un message étant diffusé d’un interlocuteur à l’autre par le biais de la méthode postMessage du Web Worker. L’invocation par la page html5 de la méthode postMessage lance de fait le Web Worker.

Que ce soit pour la page html5 ou le Web Worker, un message est réceptionné et traité sur écoute de l’événement message.

Côté page html5, il suffit d’abonner le Web Worker à l’événement message et de lui associer une fonction chargée de son traitement. Lorsque qu’un message est diffusé, un évènement message est alors levé, puis intercepté par le Web Worker qui est abonné à l’évènement. La fonction associée à l’événement est alors exécutée :

worker.addEventListener('message', function(e) {
     .....
  }, false);

ou

worker.onmessage = function(e) {
     .....
  }, false);

Côté Web Worker, le principe est identique, lorsque la page html5 enverra un message, la fonction associée à l’événement sera alors exécutée :

onmessage = function(event) {
     .....
}

 

Comment utiliser un WebWorker ?

Il suffit de créer un objet Javascript Worker prenant en paramètre le nom du fichier source javascript à exécuter :

Worker worker = new Worker("fichier_javascript.js");

Certains navigateurs comme Google Chrome n’autorisent pas d’emblée l’exécution de fichier en local, il n’autorise pas les noms de fichiers spécifiés sous la forme file://nom_fichier.js.

Dans ce cas 2 choix s’offrent à vous :
– démarrer Google Chrome avec le paramètre –allow-file-access-from-files;
– ou déployer vos fichiers sur un serveur web comme Apache.

Pour l’exécuter, il suffit d’invoquer la méthode postmessage() :

worker.postmessage();

La méthode postmessage() peut prendre une chaîne de caractères en paramètre qui constitue alors le message diffusé au Web Worker html5 :

worker.postmessage("message");

 

Un exemple pour mieux appréhender

L’exemple va consister à démarrer un Web Worker depuis une page html5 sur pression d’un bouton.

Lorsqu’il sera lancé, le Web Worker enverra toutes les secondes, l’heure courante à la page html5. Pour diffuser l’heure chaque seconde, nous utilisons la fonction javascript setInterval prenant en paramètres une fonction et une fréquence d’appel de cett même fonction exprimée en millisecondes.

La page html5 mettra à jour l’affichage de l’heure à la réception du message du Web Worker html5 comportant l’heure.

La page html5 :

<html>
<head>
<title>DEMO WEB WORKER HTML5 JAVASCRIPT</title>
</head>
<body>
<h1>DEMO 1 <strong><u>WEB WORKER HTML5 JAVASCRIPT</u></strong></h1>
<br><br>
LABEL : <label id='labelHeure'>WORKER NON DEMARRE</label>
<input type="button" id="boutonDemarrerWorker" value="Démarrer Worker">
</body>
<script>
var worker;
if( !!window.Worker ) {
  // web workers supportes
  worker = new Worker("./worker1.js");
  worker.addEventListener('message', function(e) {
	// mis a jour de l'affichage a reception du message du worker
	document.getElementById("labelHeure").innerHTML = e.data;
  }, false);
} else {
  // web workers non supportes
}

var boutonDemarrerWorker = document.getElementById("boutonDemarrerWorker");
boutonDemarrerWorker.onclick = function(e) {
  worker.postMessage();
};
</script>
</html>

Le Worker worker.js :

var intervalId;

onmessage = function(e) {
  intervalId = setInterval( envoyerDate, 1000);
};

var envoyerDate = function() {
  postMessage(new Date());
}

Le résultat :

Cliquez ici pour voir la démo Web Worker html5

 

Stopper un Web Worker html5

L’exécution d’un Web Worker peut être stoppée :
– depuis la page HTML5 lanceur du Web Worker par le biais de la méthode terminate();
– par le Worker lui-même par le biais de la méthode close().

 

Stopper un Web Worker depuis la page hôte

Le fait de stopper un Web Worker depuis la page html5 hôte le stoppe et interrompt son exécution immédiatement. Le Web Workerest tué: impossible de communiquer avec lui via la méthode postMessage; il est nécessaire de réinstancier le Worker, et de le réabonner à l’événement message.

Nous modifions l’exemple précédent en y ajoutant un bouton pour stopper le Worker qui sera arrêté depuis la page hôte :

<html>
<head>
<title>DEMO WEB WORKER HTML5 JAVASCRIPT</title>
</head>
<body>
<h1>DEMO 2 <strong><u>WEB WORKER HTML5 JAVASCRIPT</u></strong></h1>
<br>
- <strong>Démarrer Worker</strong> : Démarrer ou redémarre le Worker après qu'il ait été suspendu.
<br>
- <strong>Stopper Worker</strong> : Stopper le Worker depuis la page hote via la méthode <strong>terminate</strong>, l'instance du Worker est détruite. Il est nécessaire de réinstancier le Worker pour le relancer.
<br><br>
LABEL : <label id='labelHeure'>WORKER NON DEMARRE</label>
<input type="button" id="boutonDemarrerWorker" value="Démarrer Worker">
 
<input type="button" id="boutonStopperWorker" value="Stopper Worker">
</body>
<script>
var worker;
if( !!window.Worker ) {
  // web workers supportés
  worker = new Worker("./worker1.js");
  worker.addEventListener('message', function(e) {
    // mis à jour de l'affichage à réception du message du worker
    document.getElementById("labelHeure").innerHTML = e.data;
  }, false);
} else {
  // web workers non supportés
}
 
var boutonDemarrerWorker = document.getElementById("boutonDemarrerWorker");
boutonDemarrerWorker.onclick = function(e) {
  worker.postMessage();
};
 
var boutonStopperWorker = document.getElementById("boutonStopperWorker");
boutonStopperWorker.onclick = function(e) {
  worker.terminate();
  document.getElementById("labelHeure").innerHTML = "WORKER STOPPE";
  worker = new Worker("./worker1.js");
  worker.addEventListener('message', function(e) {
    // mis à jour de l'affichage à réception du message du worker
    document.getElementById("labelHeure").innerHTML = e.data;
  }, false);
};
</script>
</html>

Le résultat :

Cliquez ici pour voir la démo 2 Web Worker html5

 

Stopper un Web Worker par lui-même

A l’instar de la méthode terminate, la méthode close interrompt le Web Worker et en supprime l’instance.

Par rapport à l’exemple précédent:
– nous ajoutons un troisième bouton Suspendre Worker destiné à mettre en pause le Web Worker html5 : l’interrompre sans en supprimer l’instance;
– nous définissons 3 messages (start pour démarrer le worker, pause pour le suspendre et stop pour le stopper) qui sont respectivement diffusés sur le clic des boutons Démarrer Worker, Suspendre Worker et Stopper Worker.

<html>
<head>
<title>DEMO WEB WORKER HTML5 JAVASCRIPT</title>
</head>
<body>
<h1>DEMO 3 <strong><u>WEB WORKER HTML5 JAVASCRIPT</u></strong></h1>
<br>
- <strong>Démarrer Worker</strong> : Démarrer ou redémarre le <strong><u>Web Worker</u></strong> après qu'il ait été suspendu.
<br>
- <strong>Suspendre Worker</strong> : Mettre le Worker en pause, l'instance du <strong><u>Web Worker</u></strong> est toujours présente. Il peut être redémarré via le bouton <strong>Démarrer Worker</strong>.
<br>
- <strong>Stopper Worker</strong> : Stopper le <strong><u>Web Worker</u></strong>, l'instance du Worker est détruite. Il ne peut être redémarré via le bouton <strong>Démarrer Worker</strong>.
<br><br>
LABEL : <label id='labelHeure'>WORKER NON DEMARRE</label>
<input type="button" id="boutonDemarrerWorker" value="Démarrer Worker">
 
<input type="button" id="boutonSuspendreWorker" value="Suspendre Worker">
 
<input type="button" id="boutonStopperWorker" value="Stopper Worker">
</body>
<script>
var worker;
if( !!window.Worker ) {
  // web workers supportes
  worker = new Worker("./worker3.js");
  worker.addEventListener('message', function(e) {
    // mis a jour de l'affichage a reception du message du worker
    document.getElementById("labelHeure").innerHTML = e.data;
  }, false);
} else {
  // web workers non supportes
}
 
var boutonDemarrerWorker = document.getElementById("boutonDemarrerWorker");
boutonDemarrerWorker.onclick = function(e) {
  worker.postMessage("start");
};

var boutonSuspendreWorker = document.getElementById("boutonSuspendreWorker");
boutonSuspendreWorker.onclick = function(e) {
  worker.postMessage("pause");
};

var boutonStopperWorker = document.getElementById("boutonStopperWorker");
boutonStopperWorker.onclick = function(e) {
  worker.postMessage("stop");
};
</script>
</html>

Nous modifions le Web Worker afin qu’il traite chacun des 3 messages définis précédemment :
– start démarre l’envoi de la date à l’hote toutes les secondes;
– pause arrête l’envoi de la date à l’hote mais ne détruit pas le Worker html5;
– stop arrête l’envoi de la date à l’hote et détruit le Worker html5.

var intervalId;
  
onmessage = function(e) {
  if ( e.data == "start" ) {
    intervalId = setInterval( envoyerDate, 1000);
  } else if ( e.data == "pause" ) {
    clearInterval( intervalId );
	postMessage("WORKER SUSPENDU");
  } else if ( e.data == "stop" ) {
    clearInterval( intervalId );
	postMessage("WORKER STOPPE");
	close();
  }
};

var envoyerDate = function() {
	postMessage(new Date());
}

Le résultat :

Cliquez ici pour voir la démo 3 Web Worker html5

 

Importation de scripts dans un Web Worker

Parfois, vous pourriez avoir besoin d’utiliser des scripts tiers au sein de votre Web Worker. Dans ce cas vous devez utiliser l’instruction importScripts prenant en paramètres un ou plusieurs nom de scripts (les scripts à importer) sous forme de chaines de caractères séparées par des virgules :

importScripts('nom_du_fichier_du_script');

Pour importer plusieurs fichiers en une seule instruction :

importScripts('script1.js','script2.js', ...., 'scriptN.js');

 
[important]Vous savez maintenant comment utiliser un Web Worker html5 et ce qu’il est possible d’en faire. Un prochain article traitera de la gestion des erreurs. Vos remarques sont toutes les bienvenues.[/important]

 

Posté dans html5Taggé dedicated web worker, dedicated webworker, faire des jeux, javascript tutorial, multithreading, multithreading javascript, parallelisation javascript, web worker, web worker html5, web workers, webworker, webworkers, worker html5  |  3 commentaires

3 réponses à "javascript tutorial : multithreading Web Worker en Javascript et html5"

Répondre

*