Cet article nécessite une relecture rédactionnelle. Voici comment vous pouvez aider.
Cette traduction est incomplète. Aidez à traduire cet article depuis l'anglais.
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):
Envoi de messages depuis et vers un worker dédié
La communication des workers se fait via la méthode postMessage()
et le gestionnaire d'événement onmessage
. Lorsque vous voulez envoyer un message au worker, vous postez des messages comme ceci ( 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');
}
Nous avons donc ici deux éléments <input>
représentés par les variables first
et second
; lorsque la valeur de l'une est modifiée, myWorker.postMessage([first.value,second.value])
est utilisé pour envoyer des valeurs au worker, comme ici, un tableau. Vous pouvez envoyer tout type de message.
Dans le worker, nous pouvons répondre, lorsque le message est reçu, avec un gestionnaire d'événement comme celui-ci ( 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);
}
Le gestionnaire onmessage
nous permet d'exécuter du code à chaque fois qu'un message est reçu, le message lui-même étant disponible dans l'attribut data
de l'évènement message
. Ici, nous multiplions simplement les deux nombres ensemble et nous utilisons de nouveau postMessage()
pour poster le résultat vers le thread principal.
De retour dans le thread principal, nous utilisons onmessage
, pour intercepter le message envoyé par le worker:
myWorker.onmessage = function(e) {
result.textContent = e.data;
console.log('Message received from worker');
}
Ici, nous récupérons les données avec l'événement onmessage
et avec, nous définissons l'attribut textContent
du paragraphe result
, afin que l'utilisateur puisse voir le résultat du calcul.
Note : L'URI passé en paramètre au constructeur Worker
doit obéir à la same-origin policy .
Il y a actuellement un désaccord entre differents navigateurs sur l'origine des URI; Gecko 10,0 (Firefox 10.0 / Thunderbird 10.0 / SeaMonkey 2.7) et superieur ne permettent plus les données et Internet Explorer 10 ne permet pas les Blob comme un script valide pour les workers.
onmessage
et postMessage()
sont accroché au Worker
quand il est utilisé dans le script principale, mais pas lorsqu'il est utilisé dans le worker. Car le worker est de portée globale.Arrêt d'un worker
Si vous avez besoin d'arrêter immédiatement un worker en cours d'exécution, vous pouvez le faire en appelant terminate()
:
myWorker.terminate();
Le worker est stoppé immédiatement, sans possibilité de terminer ses opérations.
Les travailleurs peuvent se fermer en appelant leur propre méthode close
:
close();
Gestion des erreurs
Quand une erreur d'exécution se produit dans le worker, son gestionnaire d'événements onerror
est appelé. Il reçoit un événement nommé error
qui implémente l'interface ErrorEvent
.
L'événement ne se propage pas et est annulable; pour empêcher l'action par défaut d'avoir lieu, le travailleur peut appeler la méthode preventDefault()
.
L'événement d'erreur possède les trois domaines suivants:
message
- Un message d'erreur lisible.
filename
- Le nom du fichier de script dans lequel l'erreur est survenue.
lineno
- Le numéro de la ligne du fichier de script sur lequel l'erreur est survenue.
Instancier des Subworkers
Un worker peut engendrer plusieurs workers. Les subworkers doivent être hébergés dans le même dossier que le parent. En outre, les URI des subworkers sont résolus par rapport à l'emplacement du worker parent. Cela rend plus facile pour les subworkers de garder une trace de leurs dépendances.
Importation de scripts et bibliothèques
Les threads de worker ont accès à une fonction globale, importScripts()
, ce qui leur permet d'importer des scripts dans le même domaine que leur champ d'application. Il accepte zéro ou plusieurs URI comme paramètres de ressources pour l'importation; tous les exemples suivants sont valables:
importScripts(); /* imports nothing */
importScripts('foo.js'); /* imports just "foo.js" */
importScripts('foo.js', 'bar.js'); /* imports two scripts */
Le navigateur charge chaque script répertorié et l'exécute. Tous les objets globaux de chaque script peuvent ensuite être utilisées par le worker. Si le script ne peut pas être chargé, NETWORK_ERROR
est émit, et le code qui suit ne sera pas exécuté. Code exécuté précédemment (y compris le code reporté en utilisant window.setTimeout()
) sera toujours fonctionnelle. Les déclarations de fonctions après la méthode importScripts()
sont également conservés, puisque ceux-ci sont toujours évalués avant le reste du code.
importScripts()
. Cela se fait de façon synchrone; importScripts()
ne retourne rien jusqu'à ce que tous les scripts soient chargé et exécuté.Workers partagés
Un worker partagé est accessible par plusieurs scripts - y compris de différentes fenêtres, des iframes ou même d'autre workers - tant qu'ils ont la même origine. Dans cette section, nous allons discuter du code JavaScript tiré de l'exemple Basic shared worker; nous allons nous concentrer sur les différences entre les workers dédiés et partagés. Notez que dans cet exemple, nous avons deux pages HTML, chacune avec du code JavaScript appliquée qui utilise le même fichier worker.
Instancier un worker partagé
C'est la même démarche que pour un worker dédié, mais avec un nom de constructeur différent (voir index.html et index2.html ) - chacun doit déclarer le worker à l'aide du code suivant :
var myWorker = new SharedWorker("worker.js");
La grande différence réside dans la communication via un port
. un port est explicitement ouvert pour que les scripts pussent communiquer avec le worker (cela se fait implicitement dans le cas des workers dédiés ). La communication doit être démarré avec start()
avant que les messages puissent être affichés (voir le début des scripts multiply.js et square.js ):
myWorker.port.start();
Envoi de messages depuis et vers un worker partagé
Maintenant, les messages peuvent être envoyés au worker comme avant, mais postMessage()
doit être invoquée à travers l'objet port
(encore une fois, vous verrez des constructions similaires dans les scripts multiply.js et square.js ):
squareNumber.onchange = function() {
myWorker.port.postMessage([squareNumber.value,squareNumber.value]);
console.log('Message posted to worker');
}
Du coté worker, c'est un peu plus de complexe ( 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();
}
Tout d'abord, nous utilisons le gestionnaire d'évènement onconnect
qui s'active lorsque le méthode start()
est invoquée. Nous utilisons l'attribut port
de cet objet événement pour saisir le port et le stocker dans une variable. Ensuite, nous utilisons un gestionnaire onmessage
sur le port pour faire les calculs et retourner le résultat au thread principal.
Enfin, de retour dans le script principal nous traitons le message (de nouveau, vous verrez des constructions similaires dans les scripts multiply.js et square.js ):
myWorker.port.onmessage = function(e) {
result2.textContent = e.data[0];
console.log('Message received from worker');
}
Quand un message revient via le port du worker, nous vérifions ce type de résultat, puis nous l'insérons dans le paragraphe résultat approprié.
Que pouvez-vous faire à un worker?
Vous pouvez utiliser les fonctions JavaScript standards dans un worker, y compris:
Navigator
XMLHttpRequest
Array
,Date
,Math
, andString
Window.requestAnimationFrame
,WindowTimers.setTimeout
, andWindowTimers.setInterval
La principale chose que vous ne pouvez pas faire à partir d'un worker est d'influer directement sur la page parent. Ceci inclut la manipulation du DOM et les objets de cette page. Vous devez le faire indirectement, par l'envoi de message au script principal via DedicatedWorkerGlobalScope.postMessage
, puis actionner les changements à partir de là.
Remarque :. Pour une liste complète des fonctions disponibles pour les workers, voir Fonctions et interfaces disponibles pour les workers.
A propos de la sécurité
L'interface Worker
engendre des pérturbations au niveau du système d'exploitation.
Cependant, puisque les workers contrôlent soigneusement les points de communication avec d'autres threads, il est en réalité très difficile de causer des problèmes de concurrence. Il n'y a pas de moyen d'accéder aux composants non-threadées, ou au DOM.
Spécifications
Specification | Status | Comment |
---|---|---|
WHATWG HTML Living Standard | Standard évolutif | No change from Web Workers. |
Web Workers | Brouillon de l'éditeur | Initial definition. |
Navigateur compatible
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 | — | — |
Note navigateur
- Chrome/Opéra donne une erreur "
Uncaught SecurityError: Failed to construct 'Worker': Script at 'file:///Path/to/worker.js' cannot be accessed from origin 'null'.
." lorsque vous essayez d'exécuter un worker local. Il doit être sur un domaine propre. - Pour Safari 7.1.2, vous pouvez appeler
console.log
à partir d'un worker, mais il ne sera inactif. Les anciennes versions de Safari ne vous permettent pas d'appelerconsole.log
à partir d'un worker.
Voir aussi
- interface
Worker
- interface
SharedWorker
- Functions available to workers
- Advanced concepts and examples
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
)
Allez 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.