| [ Web Proxy ] |
| Viewing: https://developer.mozilla.org/fr/docs/Web/API/Web_Workers_API/Using_web_workers | [Back] [Original] |
Get to know MDN better
Cette page a t traduite partir de l'anglais par la communaut. Vous pouvez contribuer en rejoignant la communaut francophone sur MDN Web Docs.
Les Web Workers sont un outil permettant au contenu web d'excuter des scripts dans des tches (threads) d'arrire-plan. Le thread associ au worker peut raliser des tches sans qu'il y ait d'interfrence avec l'interface utilisateur. De plus, les web workers peuvent raliser des oprations d'entre/sortie grce XMLHttpRequest (bien que les attributs responseXML et channel soient ncessairement vides dans ces cas). Une fois cr, un worker peut envoyer des messages au code JavaScript qui l'a cr. De mme, le script initial peut envoyer des messages au worker. Cette communication s'effectue grce des gestionnaires d'vnements. Dans cet article, nous verrons une introduction l'utilisation des web workers.
Un worker est un objet cr l'aide d'un constructeur (par exemple Worker()) et qui excute un fichier JavaScript donn. Ce fichier contient le code qui sera excut par le thread du worker. Les workers sont excuts dans un contexte global qui n'est pas celui du document (gnralement window). Aussi, si, dans un worker, on utilise window pour accder la porte globale (plutt que self), cela provoquera une erreur.
Le contexte du worker est reprsent par un objet DedicatedWorkerGlobalScope pour les workers ddis et par un objet SharedWorkerGlobalScope sinon. Un worker ddi est uniquement accessible au travers du script qui l'a dclench tandis qu'un worker partag peut tre utilis par diffrents scripts.
Note : Voir la page d'entre pour l'API Web Workers pour consulter la documentation de rfrence sur les workers et d'autres guides.
Il est possible d'excuter n'importe quel code JavaScript dans le thread du worker, l'exception des mthodes de manipulation du DOM ou de certaines proprits et mthodes rattaches window. On notera cependant qu'on peut tout fait utiliser certaines API rendues disponibles via window comme les WebSockets, les API de stockage de donnes telles que IndexedDB. Pour plus de dtails, voir les fonctions et classes disponibles au sein des workers.
Les donnes sont changes entre le thread du worker et le thread principal par l'intermdiaire de messages. Chaque partie peut envoyer des messages l'aide de la mthode postMessage() et ragir aux messages reus grce au gestionnaire d'vnement onmessage (le message sera contenu dans l'attribut data de l'vnement message associ). Les donnes sont copies dans le message, elles ne sont pas partages.
Les workers peuvent galement dclencher la cration d'autres workers tant que ceux-ci restent hbergs sur la mme origine que la page parente. De plus, les workers pourront utiliser XMLHttpRequest pour effectuer des oprations rseau mais les attributs responseXML et channel de XMLHttpRequest renverront ncessairement null.
Comme indiqu plus haut, un worker ddi n'est accessible qu'au travers du script qui l'a initi. Dans cette section, nous tudierons le code JavaScript de notre exemple de worker ddi simple. Dans cet exemple, nous souhaitons multiplier deux nombres. Ces nombres sont envoys un worker ddi puis le rsultat est renvoy la page et affich.
Cet exemple est assez simple mais permet d'introduire les concepts de base autour des workers. Nous verrons certains dtails plus avancs dans la suite de cet article.
Afin de grer une meilleure amlioration progressive, une rtro-compatibilit et de prsenter des messages d'erreur adquats, il pourra tre utile d'envelopper le code relatif au worker de la faon suivante (main.js) :
if (window.Worker) {
...
}
La cration d'un nouveau worker est assez simple. On appellera le constructeur Worker() en indiquant l'URI du script excuter dans le thread associ au worker (main.js) :
var monWorker = new Worker("worker.js");
L'intrt principal des workers repose sur l'change de messages l'aide de la mthode postMessage() et grce au gestionnaire d'vnement onmessage. Lorsqu'on souhaite envoyer un message au worker, on enverra des messages de la faon suivante (main.js) :
premierNombre.onchange = function () {
monWorker.postMessage([premierNombre.value, deuxiemeNombre.value]);
console.log("Message envoy au worker");
};
deuxiemeNombre.onchange = function () {
monWorker.postMessage([premierNombre.value, deuxiemeNombre.value]);
console.log("Message envoy au worker");
};
Ici, nous disposons de deux lments <input> reprsents par les variables premierNombre et deuxiemeNombre. Lorsque l'un de ces deux champs est modifi, on utilise monWorker.postMessage([premierNombre.value, deuxiemeNombre.value]) afin d'envoyer les deux valeurs au worker dans un tableau. Les messages peuvent tre utiliss pour changer n'importe quel type de valeur.
Dans le worker, on peut ragir au message reu grce un gestionnaire d'vnement comme celui-ci (worker.js) :
onmessage = function (e) {
console.log("Message reu depuis le script principal.");
var workerResult = "Rsultat : " + e.data[0] * e.data[1];
console.log("Envoi du message de retour au script principal");
postMessage(workerResult);
};
Le gestionnaire onmessage permet d'excuter du code lorsqu'un message est reu. Le message mme est disponible grce l'attribut data de l'vnement. Dans cet exemple, nous multiplions simplement les deux nombres avant d'utiliser postMessage() nouveau afin d'envoyer le rsultat via un message destin au thread principal.
De retour dans le thread principal, nous pouvons utiliser onmessage nouveau pour ragir la rponse provenant du worker :
monWorker.onmessage = function (e) {
resultat.textContent = e.data;
console.log("Message reu depuis le worker");
};
Ici, nous rcuprons les donnes grce l'attribut data de l'vnement et nous mettons jour le contenu du paragraphe avec l'attribut textContent de l'lment. Ainsi, l'utilisateur peut visualiser le rsultat du calcul.
Note :
On notera que onmessage et postMessage() doivent tre rattachs un objet Worker lorsqu'ils sont utiliss depuis le thread principal (ici, c'tait monWorker) mais pas lorsqu'ils sont employs depuis le worker. En effet, dans le worker, c'est le worker qui constitue la porte globale et qui met disposition ces mthodes.
Note : Lorsqu'un message est envoy d'un thread l'autre, ses donnes sont copies. Elles ne sont pas partages. Voir ci-aprs pour plus d'explications ce sujet.
Si on doit arrter un worker immdiatement, on pourra utiliser la mthode terminate depuis le thread principal :
monWorker.terminate();
Lorsque cette mthode excut, le thread associ au worker est tu immdiatement.
Lorsqu'une erreur d'excution se produit avec le worker, son gestionnaire d'vnement onerror est appel et reoit un vnement error qui implmente l'interface ErrorEvent.
Cet vnement ne bouillonne (bubble) pas et peut tre annul. Pour empcher les consquences par dfaut, on pourra utiliser la mthode preventDefault() rattache l'vnement d'erreur.
L'vnement dcrivant l'erreur possde notamment trois proprits intressantes :
messageUn message d'erreur comprhensible par un humain.
filenameLe nom du fichier pour lequel l'erreur s'est produite.
linenoLe numro de ligne au sein du fichier responsable de l'erreur.
Les workers peuvent galement engendrer d'autres workers. Ces workers-fils doivent tre hbergs sur la mme origine que la page initiale. De plus, les URI des workers-fils sont rsolues relativement l'emplacement du worker pre (plutt que par rapport la page parente). Ces contraintes permettent de simplifier le suivi des dpendances.
Les threads d'excution des workers peuvent accder la fonction globale importScripts(), qui leur permet d'importer des scripts. Cette fonction prend zro plusieurs URL en paramtres et importe les ressources associes. Voici quelques exemples valides :
importScripts(); /* n'importe rien */
importScripts("toto.js"); /* importe uniquement "toto.js" */
importScripts("toto.js", "truc.js"); /* importe deux scripts */
importScripts(
"//example.com/hello.js",
); /* importe un script d'une autre origine */
Lors d'un import, le navigateur chargera chacun des scripts puis l'excutera. Chaque script pourra ainsi mettre disposition des objets globaux qui pourront tre utiliss par le worker. Si le script ne peut pas tre charg, une exception NETWORK_ERROR sera leve et le code assic ne sera pas excut. Le code excut prcdemment (y compris celui-ci report l'aide de window.setTimeout()) continuera cependant d'tre fonctionnel. Les dclarations de fonction situes aprs importScripts() sont galement excutes car values avant le reste du code.
Note :
Les scripts peuvent tre tlchargs dans n'importe quel ordre mais ils seront excuts dans l'ordre des arguments passs importScripts() . Cet excution est effectue de faon synchrone et importScripts() ne rendra pas la main tant que l'ensemble des scripts n'auront pas t chargs et excuts.
Un worker partag est accessible par plusieurs scripts (mme si ceux-ci proviennent de diffrentes fentres, iframes voire d'autres workers). Dans cette section, nous illustrerons les concepts l'aide de l'exemple simple d'un worker partag. Cet exemple est semblable l'exemple utilis pour le worker ddi. Il diffre car il possde deux fonctions, gres par deux fichiers de script distincts : une fonction permettant de multiplier deux nombres et une fonction permettant d'lever un nombre au carr. Les deux scripts utilisent le mme worker pour raliser le calcul demand.
Ici, nous nous intresserons particulirement aux diffrences entre les workers ddis et les workers partags. Dans cet exemple, nous aurons deux pages HTML, chacune utilisant du code JavaScript employant le mme worker.
Note : Si on peut accder un worker partag depuis diffrents contextes de navigations, ces contextes de navigation doivent nanmoins partager la mme origine (mme protocole, mme hte, mme port).
Note : Dans Firefox, les workers partags ne peuvent pas tre partags entre les documents chargs en navigation prive et les documents chargs en navigation classique (bug Firefox 1177621).
La cration d'un nouveau worker partag est assez semblable la cration d'un worker ddi. On utilise alors un constructeur diffrent :
var monWorker = new SharedWorker("worker.js");
Une diffrence fondamentale avec les workers ddis est l'utilisation d'un objet port pour la communication. Un port sera explicitement ouvert pour tre utilis afin de communiquer avec le worker (dans le cas des workers ddis, ce port est ouvert implicitement).
La connexion au port doit tre dmarre implicitement avec l'utilisation du gestionnaire d'vnement onmessage ou explicitement avec la mthode start() avant qu'un message soit envoy. On utilisera uniquement start() si l'vnement message est dtect avec la mthode addEventListener().
Note :
Lorsqu'on utilise la mthode start() afin d'ouvrir le port de connexion, celle-ci doit tre appele de part et d'autre (depuis le thread parent et depuis le worker) si on souhaite disposer d'une connexion bidirectionnelle.
On peut alors envoyer des messages au worker. Dans le cas d'un worker partag, la mthode postMessage() doit tre appele via l'objet port (l aussi, vous pouvez tudier le code de multiply.js et square.js) :
carreNombre.onchange = function () {
monWorker.port.postMessage([carreNombre.value, carreNombre.value]);
console.log("Message envoy au worker");
};
Du ct du worker, les choses sont galement lgrement plus compliques (voir worker.js) :
onconnect = function (e) {
var port = e.ports[0];
port.onmessage = function (e) {
var workerResult = "Rsultat : " + e.data[0] * e.data[1];
port.postMessage(workerResult);
};
};
On commence par utiliser le gestionnaire onconnect afin de dclencher du code la connexion au port (c'est--dire lorsque le gestionnaire onmessage est dclar depuis le thread parent ou lorsque la mthode start() est invoque explicitement depuis le thread parent).
On utilise l'attribut ports de l'vnement afin de rcuprer le port utilis et on le place dans une variable.
Ensuite, sur ce port, on ajoute un gestionnaire d'vnement pour l'vnement message afin de faire les calculs et de renvoyer le rsultat au thread principal. En dfinissant ce gestionnaire pour message dans le thread du worker, on ouvre implicitement le port pour la connexion au thread parent : il n'est donc pas ncessaire d'invoquer port.start().
Enfin, dans le script de la page, on gre le message du rsultat (voir multiply.js et square.js):
monWorker.port.onmessage = function (e) {
result2.textContent = e.data;
console.log("Message reu depuis le worker");
};
Lorsqu'un message provient du port associ au worker, on vrifie son type puis on insre le rsultat dans le paragraphe associ.
L'interface Worker engendre des threads au sens du systme d'exploitation. Certains dveloppeurs avertis pourront se demander si cette communication base de threads ne peut pas gnrer d'effets indsirables tels que des situations de comptition (race conditions).
Toutefois, la communication entre les web workers est contrle explicitement dans les scripts et il n'y a pas d'accs aux composants ou au DOM qui ne seraient pas srs ce niveau. De plus, la communication entre les threads s'effectue par recopie de donnes. Aussi, s'il n'est thoriquement pas impossible de ne pas avoir de tels problmes, il faudrait les chercher pour les provoquer.
Les workers disposent de leur propre contexte d'excution, distinct de celui du document qui les a crs. Aussi, en gnral, les workers ne sont pas grs par la politique de scurit de contenu du document (ou du worker parent) responsable de leur cration. Ainsi, si un document est servi avec l'en-tte suivant :
Content-Security-Policy: script-src 'self'
Cette rgle empchera n'importe quel script inclus dans le document d'utiliser eval(). Toutefois, si le script gnre un worker, le code excut par ce worker pourra utiliser eval().
Pour appliquer une rgle de scurit au worker, il faudra fournir un en-tte Content-Security-Policy appropri pour la requte responsable du service du script du worker.
Si l'origine du script du worker est un identifiant global unique (si son URL utilise le schma data:// ou blob:// par exemple), le worker hritera du CSP associ au document responsable de sa cration.
Les donnes changes entre le document principal et les workers sont copies et non partages. Lorsqu'ils sont envoys au worker, les objets sont srialiss (puis dsrialiss leur rception). La page et le worker ne partagent pas le mme exemplaire et on a donc deux versions d'une part et d'autre. La plupart des navigateurs implmentent cette approche avec une clonage structurel.
Pour illustrer ce point, on prendra une fonction intitule emulateMessage() et qui simule le comportement d'une valeur clone (pas partage) avec un worker attach la page principale et rciproquement :
function emulateMessage(vVal) {
return eval("(" + JSON.stringify(vVal) + ")");
}
// Tests
// test #1
var example1 = new Number(3);
console.log(typeof example1); // object
console.log(typeof emulateMessage(example1)); // number
// test #2
var example2 = true;
console.log(typeof example2); // boolean
console.log(typeof emulateMessage(example2)); // boolean
// test #3
var example3 = new String("Hello World");
console.log(typeof example3); // object
console.log(typeof emulateMessage(example3)); // string
// test #4
var example4 = {
name: "John Smith",
age: 43,
};
console.log(typeof example4); // object
console.log(typeof emulateMessage(example4)); // object
// test #5
function Animal(sType, nAge) {
this.type = sType;
this.age = nAge;
}
var example5 = new Animal("Cat", 3);
console.log(example5.constructor); // Animal
console.log(emulateMessage(example5).constructor); // Object
Une valeur qui est clone et non partage est appele un message. Les messages peuvent tre envoys et reus grce postMessage() et au gestionnaire d'vnement pour message (dont l'attribut data contiendra les donnes copies).
example.html (page principale) :
var myWorker = new Worker("my_task.js");
myWorker.onmessage = function (oEvent) {
console.log("Worker said : " + oEvent.data);
};
myWorker.postMessage("ali");
my_task.js (le code du worker) :
postMessage("I'm working before postMessage('ali').");
onmessage = function (oEvent) {
postMessage("Hi " + oEvent.data);
};
L'algorithme de clonage structurel permet de srialiser aussi bien des donnes JSON que d'autres formats et permet notamment de grer les rfrences circulaires (ce que JSON ne permet pas de grer nativement).
Chrome 17+ et Firefox 18+ permettent galement d'changer certains types d'objet (qualifis de transfrables et qui implmentent l'interface Transferable) avec des workers et haute performance. Les objets transfrables sont passs d'un contexte l'autre avec une opration zero-copy qui permet d'obtenir des amliorations sensibles lors de l'change de donnes volumineuses. On peut voir cela comme un passage de rfrence du monde C/C++ mais les donnes qui sont transfres depuis le contexte appelant ne sont plus disponibles dans ce contexte aprs le transfert. La proprit des donnes est transfre au nouveau contexte. Ainsi, lorsqu'on transfre un objet ArrayBuffer depuis l'application principale vers le worker, l'objet ArrayBuffer de dpart est nettoy et ne peut plus tre utilis, son contenu est (littralement) transfr au contexte du worker.
// Crer un fichier de 32MB et le remplir.
var uInt8Array = new Uint8Array(1024 * 1024 * 32); // 32MB
for (var i = 0; i < uInt8Array.length; ++i) {
uInt8Array[i] = i;
}
worker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]);
Note : Pour plus d'informations quant aux objets transfrables, aux performances associes et la dtection de ces fonctionnalits, on pourra lire Transferable Objects: Lightning Fast.
Il n'existe pas de mthode standard pour embarquer le code d'un worker dans une page web la faon des lments <script>. Toutefois, un lment <script>, qui ne possde pas d'attribut src, qui possde un attribut type ne correspondant pas un type MIME excutable pourra tre considr comme un bloc de donnes pouvant tre utilis par JavaScript. Ces blocs de donnes sont une fonctionnalit HTML5 qui permet de transporter n'importe quelle donne textuelle. On pourrait donc embarquer un worker de cette faon :
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Exemple MDN - Worker embarqu</title>
<script type="text/js-worker">
// Ce script ne sera pas analys par le moteur JS car
// son type MIME est text/js-worker.
var maVar = 'Coucou monde !';
// Reste du code du worker.
</script>
<script type="text/javascript">
// Ce script sera analys par le moteur JS car son type MIME
// est text/javascript.
function pageLog(sMsg) {
// On utilise un fragment afin que le navigateur ne rende/peigne
// qu'une seule fois.
var oFragm = document.createDocumentFragment();
oFragm.appendChild(document.createTextNode(sMsg));
oFragm.appendChild(document.createElement("br"));
document.querySelector("#logDisplay").appendChild(oFragm);
}
</script>
<script type="text/js-worker">
// Ce script ne sera pas analys par le moteur JS car son type
// MIME est text/js-worker.
onmessage = function(oEvent) {
postMessage(myVar);
};
// Reste du code du worker
</script>
<script type="text/javascript">
// Ce script sera analys par le moteur JS car son type MIME est
// text/javascript
var blob = new Blob(
Array.prototype.map.call(
document.querySelectorAll("script[type='text\/js-worker']"),
function (oScript) {
return oScript.textContent;
},
),
{ type: "text/javascript" },
);
// On cre une nouvelle proprit document.worker qui contient
// tous les scripts "text/js-worker".
document.worker = new Worker(window.URL.createObjectURL(blob));
document.worker.onmessage = function (oEvent) {
pageLog("Received: " + oEvent.data);
};
// On dmarre le worker.
window.onload = function () {
document.worker.postMessage("");
};
</script>
</head>
<body>
<div id="logDisplay"></div>
</body>
</html>
Le worker embarqu est dsormais inject dans la proprit document.worker.
On notera galement qu'on peut convertir une fonction en un Blob et gnrer une URL d'objet vers ce blob. Par exemple :
function fn2workerURL(fn) {
var blob = new Blob(["(" + fn.toString() + ")()"], {
type: "application/javascript",
});
return URL.createObjectURL(blob);
}
Dans cette section nous voyons d'autres exemples d'application.
Les workers sont notamment utiles pour raliser des oprations de traitement intensives sans bloquer pour autant le thread responsable de l'interface utilisateur. Dans cet exemple, on utilise un worker afin de calculer la suite de Fibonacci.
Le code JavaScript suivant sera enregistr dans le fichier "fibonacci.js" auquel on fera rfrence dans le document HTML ci-aprs.
var results = [];
function resultReceiver(event) {
results.push(parseInt(event.data));
if (results.length == 2) {
postMessage(results[0] + results[1]);
}
}
function errorReceiver(event) {
throw event.data;
}
onmessage = function (event) {
var n = parseInt(event.data);
if (n == 0 || n == 1) {
postMessage(n);
return;
}
for (var i = 1; i <= 2; i++) {
var worker = new Worker("fibonacci.js");
worker.onmessage = resultReceiver;
worker.onerror = errorReceiver;
worker.postMessage(n - i);
}
};
On a dfini la proprit onmessage avec une fonction qui recevra les messages envoys au worker via postMessage(). On initie alors la rcursion et on dclenche des copies du worker afin de grer chacune des itrations lies au calcul.
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Test threads fibonacci</title>
</head>
<body>
<div id="result"></div>
<script language="javascript">
var worker = new Worker("fibonacci.js");
worker.onmessage = function (event) {
document.getElementById("result").textContent = event.data;
dump("Got: " + event.data + "\n");
};
worker.onerror = function (error) {
console.error("Worker error: " + error.message + "\n");
throw error;
};
worker.postMessage("5");
</script>
</body>
</html>
Dans la page web, on cre un lment div avec l'identifiant result. C'est cet lment qui sera utilis afin d'afficher le rsultat.
Ensuite, on lance le worker. Aprs avoir initi le worker, on configure le gestionnaire d'vnement onmessage afin d'afficher le rsultat via le div. On configure galement le gestionnaire onerror afin d'afficher l'erreur de la console.
Enfin, on envoie un message au worker afin de le dmarrer.
Les ordinateurs dots de plusieurs coeurs se gnralisent et il peut s'avrer utile de fragmenter une tche complexe entre diffrents workers afin de tirer parti des diffrents coeurs du processeur.
En plus des web workers (ddis et partags), il existe d'autres types de workers :
La plupart des fonctionnalits JavaScript standard peuvent tre utilises dans les web workers, dont :
NavigatorXMLHttpRequestArray, Date, Math et StringWindowTimers.setTimeout et WindowTimers.setIntervalEn revanche, un worker ne pourra pas directement manipuler la page parente et notamment le DOM et les objets de la page. Il faudra effectuer ce traitement indirectement, via des messages.
Note : Pour avoir une liste exhaustive des fonctionnalits disponibles pour les workers, voir les fonctions et interfaces disponibles pour les workers.
| Spcification |
|---|
| HTML # workers |
Cette page a t modifie le 7 sept. 2026 par les contributeurices du MDN.
Certaines parties de ce contenu sont protges par le droit d'auteur 19982026 des contributeurs individuels de mozilla.org. Contenu disponible sous une licence Creative Commons.
| Web Proxy Viewer | New URL | Original Page |