Utilisation des web workers

Cette traduction est en cours.

Traduction en cours de la page en anglais  Using_web_workers

Les Web Workers fournissent un moyen simple d'exécuter des scripts dans des threads en arrière-plan d'un contenu web. Le thread worker peut réaliser des tâches sans interférer avec l'interface utilisateur. De plus, ils peuvent réaliser des E/S en utilisant XMLHttpRequest (même si les attributs responseXML  et channel restent toujours null). Une fois créé, un worker peut envoyer des messages à son thread parent par l'intermédiaire du gestionnaire d'événement spécifié à sa création. Cet article propose une introduction aux workers dédiés et aux workers partagés.

Un worker dédié est seulement accessible à partir du script qui l'a préalablement créé, tandis que les workers partagés peuvent être interrogés par de multiples scripts.

Les workers s'exécutent dans un un contexte global différent du contexte de la fenêtre courante. Ainsi, utiliser le raccourci window pour obtenir le contexte global courant (au lieu de self) au sein d'un Worker retournera une erreur.

Remarque : consultez la page d'introduction à l'API Web Workers pour la documentation de référence sur les workers et des guides complémentaires.

Démos basiques

Pour faciliter la prise en main des workers, nous avons réalisé quelques démos basiques de workers dédiés et partagés :

  • Basic dedicated worker example (run dedicated worker) : vous autorise à entrer deux nombres qui seront multipliés ensemble. Les nombres sont envoyés à un worker, multipliés entre eux, et le résultat est renvoyé par le worker à la page pour enfin être affiché.
  • Basic shared worker example (run shared worker) : très similaire, à l'exception près que deux fonctions sont disponibles et gérées par différents fichiers de script javascript : multiplying two numbers, ou squaring a number. Les deux scripts utilisent le même worker pour effectuer le calcul.

Les deux démonstrations ressemblent respectivement à ceci:

Nous allons désormais regarder les bases des workers dédiés et des workers partagés.

Dedicated workers (workers dédiés)

Comme précisé au-dessus, un worker dédié est accessible uniquement par le script qui l'a appelé. Dans cette section, nous utiliserons le JavaScript trouvé dans notre Basic dedicated worker example.

Détection de worker

Afin de mieux contrôler les erreur et la rétrocompatiblité, une bonne idée est d'envelopper votre code de worker de cette manière (main.js):

if (!!window.Worker) {

  ...

}
 
 
 
 
 

Instancier un dedicated worker (worker dédié)

Créer un nouveau worker est simple. Vous devez juste appeler le constructeur Worker() , en spécifiant l'URI du script à exécuter dans le thread du worker (main.js):

var myWorker = new Worker("worker.js");
 

Sending messages to and from a dedicated worker

The magic of workers happens via the postMessage() method and the onmessage event handler. When you want to send a message to the worker, you post messages to it like this (main.js):

first.onchange = function() {
  myWorker.postMessage([first.value,second.value]);
  console.log('Message posted to worker');
}

second.onchange = function() {
  myWorker.postMessage([first.value,second.value]);
  console.log('Message posted to worker');
}
 
 
 
 
 
 
 
 
 

So here we have two <input> elements represented by the variables first and second; when the value of either is changed, myWorker.postMessage([first.value,second.value]) is used to send the value inside both to the worker, as an array. You can send pretty much anything you like in the message.

In the worker, we can respond when the message is received by writing an event handler block like this (worker.js):

onmessage = function(e) {
  console.log('Message received from main script');
  var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
  console.log('Posting message back to main script');
  postMessage(workerResult);
}
 
 
 
 
 
 

The onmessage handler allows us to run some code whenever a message is received, with the message itself being available in the message event's data attribute. Here we simply multiply together the two numbers then use postMessage() again, to post the result back to the main thread.

Back in the main thread, we use onmessage again, to respond to the message sent back from the worker:

myWorker.onmessage = function(e) {
  result.textContent = e.data;
  console.log('Message received from worker');
}
 
 
 
 

Here we grab the message event data and set it as the textContent of the result paragraph, so the user can see the result of the calculation.

Note : The URI passed as parameter of the Worker constructor must obey the same-origin policy .

There is currently disagreement among browsers vendors on what URIs are of the same-origin; Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0 / SeaMonkey 2.7) and later do allow data URIs and Internet Explorer 10 does not allow Blob URIs as a valid script for workers.

Note: Notice that onmessage and postMessage() need to be hung off the Worker object when used in the main script thread, but not when used in the worker. This is because inside the worker, the worker is effectively the global scope.
Note: When a message is passed between main thread and worker, it is copied or "transferred" (moved), not shared. Read Passing data: copied, not shared for a much more thorough explanation.

Terminating a worker

If you need to immediately terminate a running worker, you can do so by calling the worker's terminate() method:

myWorker.terminate();
 

The worker thread is killed immediately without an opportunity to complete its operations or clean up after itself.

Workers may close themselves by calling their own close method:

close();
 

Handling errors

When a runtime error occurs in worker, its onerror event handler is called. It receives an event named error which implements the ErrorEvent interface.

The event doesn't bubble and is cancelable; to prevent the default action from taking place, the worker can call the error event's preventDefault() method.

The error event has the following three fields that are of interest:

message
A human-readable error message.
filename
The name of the script file in which the error occurred.
lineno
The line number of the script file on which the error occurred.

Spawning subworkers

Workers may spawn more workers if they wish. So-called sub-workers must be hosted within the same origin as the parent page. Also, the URIs for subworkers are resolved relative to the parent worker's location rather than that of the owning page. This makes it easier for workers to keep track of where their dependencies are.

Importing scripts and libraries

Worker threads have access to a global function, importScripts(), which lets them import scripts in the same domain into their scope. It accepts zero or more URIs as parameters to resources to import; all of the following examples are valid:

importScripts();                        /* imports nothing */
importScripts('foo.js');                /* imports just "foo.js" */
importScripts('foo.js', 'bar.js');      /* imports two scripts */
 

The browser loads each listed script and executes it. Any global objects from each script may then be used by the worker. If the script can't be loaded, NETWORK_ERROR is thrown, and subsequent code will not be executed. Previously executed code (including code deferred using window.setTimeout()) will still be functional though. Function declarations after the importScripts() method are also kept, since these are always evaluated before the rest of the code.

Note: Scripts may be downloaded in any order, but will be executed in the order in which you pass the filenames into importScripts() . This is done synchronously; importScripts() does not return until all the scripts have been loaded and executed.

Shared workers

A shared worker is accessible by multiple scripts — even if they are being access by different windows, iframes or even workers — as long as they are on the same origin. In this section we'll discuss the JavaScript found in our Basic shared worker example; we'll concentrate on the differences between dedicated and shared workers. Note that in this example we have two HTML pages, each with JavaScript applied that uses the same single worker file.

Spawning a shared worker

Spawning a new worker is pretty much the same as with a dedicated worker, but with a different constructor name (see index.html and index2.html) — each one has to spin up the worker using code like the following:

var myWorker = new SharedWorker("worker.js");
 

One big difference is that with a shared worker you have to communicate via a port object — an explicit port is opened that the scripts can communicate with the worker through (this is done implicitly in the case of dedicated workers). This needs to be started with the start() method before any messages can be posted (see the start of both multiply.js and square.js):

myWorker.port.start();
 

Sending messages to and from a shared worker

Now messages can be sent to the worker as before, but the postMessage() method has to be invoked through the port object (again, you'll see similar constructs in both multiply.js and square.js):

squareNumber.onchange = function() {
  myWorker.port.postMessage([squareNumber.value,squareNumber.value]);
  console.log('Message posted to worker');
}
 
 
 
 

Now, on to the worker. There is a bit more complexity here as well (worker.js):

onconnect = function(e) {
  var port = e.ports[0];
  port.onmessage = function(e) {
    var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
    port.postMessage(workerResult);
  }
  port.start();
}
 
 
 
 
 
 
 
 

First, we use an onconnect handler to fire code when a connection to the port happens (i.e. when the start() method was invoked and a message was sent.) We use the ports attribute of this event object to grab the port and store it in a variable. Next, we use an onmessage handler on the port to do the calculation and return the result to the main thread.

Finally, back in the main script we deal with the message (again, you'll see similar constructs in both multiply.js and square.js):

myWorker.port.onmessage = function(e) {
  result2.textContent = e.data[0];
  console.log('Message received from worker');
}
 
 
 
 

When a message comes back through the port from the worker, we check what result type it is, then insert the calculation result inside the appropriate result paragraph.

What can you do in a worker?

You can use most standard JavaScript features inside a web worker, including:

The main thing you can't do in a Worker is directly affect the parent page. This includes manipulating the DOM and using that page's objects. You have to do it indirectly, by sending a message back to the main script via DedicatedWorkerGlobalScope.postMessage, then actioning the changes from there.

Note: For a complete list of functions available to workers, see Functions and interfaces available to workers.

About thread safety

The Worker interface spawns real OS-level threads, and mindful programmers may be concerned that concurrency can cause “interesting” effects in your code if you aren't careful.

However, since web workers have carefully controlled communication points with other threads, it's actually very hard to cause concurrency problems. There's no access to non-threadsafe components, or the DOM. And you have to pass specific data in and out of a thread through serialized objects. So you have to work really hard to cause problems in your code.

Specifications

Specification Status Comment
WHATWG HTML Living Standard Standard évolutif No change from Web Workers.
Web Workers Candidat au statut de recommandation Initial definition.

Browser compatibility

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
Basic support 4 Unknown (3.5) 10.0 10.6 4
Shared workers 4 29 (29) Pas de support 10.6 4
Passing data using structured cloning 13 8 (8) 10.0 11.5 6
Passing data using  transferable objects 17 webkit
21
18 (18) Pas de support 15 6
Global URL 10 as webkitURL
23
21 (21) 11 15 6 as webkitURL
Feature Android Chrome Mobile Firefox Mobile (Gecko) Firefox OS (Gecko) IE Phone Opera Mobile Safari Mobile
Basic support 4.4 4 3.5 1.0.1 10.0 11.5 5.1
Shared workers 4 8 1.0.1 Pas de support
Passing data using structured cloning 4 8 1.0.1 Pas de support
Passing data using  transferable objects 18 1.0.1 Pas de support

Browser notes

  • Chrome/Opera give an error "Uncaught SecurityError: Failed to construct 'Worker': Script at 'file:///Path/to/worker.js' cannot be accessed from origin 'null'." when you try to run a worker locally. It needs to be on a proper domain.
  • As of Safari 7.1.2, you can call console.log from inside a worker, but it won't print anything to the console. Older versions of Safari don't allow you to call console.log from inside a worker.

See also

 

Les Web Workers permettent d'exécuter du code en tâche de fond. Une fois créé, un worker peut envoyer des messages à son processus parent en envoyant des messages qui seront réceptionnés par un gestionnaire d'événement spécifié à la création.

Un thread worker peut effectuer une tâche sans interférer sur l'interface utilisateur. De plus, ils peuvent utiliser XMLHttpRequest pour les flux I/O (néanmoins les attributs responseXML et channel seront null)

Aller sur la page Worker pour avoir une documentation complète sur le sujet. Cet article a pour but de présenter des exemples et des détails supplémentaires. Pour une liste des fonctions disponibles sur les workers : Fonctions disponibles pour les workers

Invoquer un worker

Il est très facile de créer un worker. Tout ce que vous avez besoin est d'appeller le constructeur Worker(), spécifier l'URI du script à exécuter dans le thread du worker, et, si vous souhaitez pouvoir recevoir des informations du worker, instancier l'attribut onmessage avec une fonction de gestion d'événement.

 

Étiquettes et contributeurs liés au document

Étiquettes : 
Dernière mise à jour par : goofy_bz,