From 39f2114f9797eb51994966c6bb8ff1814c9a4da8 Mon Sep 17 00:00:00 2001 From: Florian Merz Date: Thu, 11 Feb 2021 12:36:08 +0100 Subject: unslug fr: move --- .../advanced_concepts_and_examples/index.html | 423 -------------- .../algorithme_clonage_structure/index.html | 153 ----- .../index.html | 240 ++++++++ .../structured_clone_algorithm/index.html | 153 +++++ .../web_workers_api/using_web_workers/index.html | 633 +++++++++++++++++++++ .../utilisation_des_web_workers/index.html | 633 --------------------- 6 files changed, 1026 insertions(+), 1209 deletions(-) delete mode 100644 files/fr/web/api/web_workers_api/advanced_concepts_and_examples/index.html delete mode 100644 files/fr/web/api/web_workers_api/algorithme_clonage_structure/index.html create mode 100644 files/fr/web/api/web_workers_api/functions_and_classes_available_to_workers/index.html create mode 100644 files/fr/web/api/web_workers_api/structured_clone_algorithm/index.html create mode 100644 files/fr/web/api/web_workers_api/using_web_workers/index.html delete mode 100644 files/fr/web/api/web_workers_api/utilisation_des_web_workers/index.html (limited to 'files/fr/web/api/web_workers_api') diff --git a/files/fr/web/api/web_workers_api/advanced_concepts_and_examples/index.html b/files/fr/web/api/web_workers_api/advanced_concepts_and_examples/index.html deleted file mode 100644 index b925ca7f4b..0000000000 --- a/files/fr/web/api/web_workers_api/advanced_concepts_and_examples/index.html +++ /dev/null @@ -1,423 +0,0 @@ ---- -title: Concepts avancés et exemples -slug: Web/API/Web_Workers_API/Advanced_concepts_and_examples -translation_of: Web/API/Web_Workers_API/Using_web_workers -translation_of_original: Web/API/Web_Workers_API/Advanced_concepts_and_examples ---- -
-

Cet article fournit de nombreux détails et maints exemples pour illustrer les concepts avancés des web workers.

-
- -

Passage de données : copie, et non partage

- -

Les données passées entre la page principale et les workers sont copiées, et non partagées. Les objets sont sérialisées au moment où ils sont confiés au worker, et consécutivement désérialisés à l'autre bout. La page et le worker ne partagent pas la même instance, ainsi au final une copie est créée de chaque côté. La plupart des navigateurs implémentent cette caractéristique en tant que clonage structuré.

- -

Avant de poursuivre, créons à des fins didactiques une fonction nommée emulateMessage(), avec pour objectif de simuler le comportement d'une valeur qui est clonée et non partagée durant le passage du worker à la page principale ou inversement :

- -
function emulateMessage (vVal) {
-    return eval("(" + JSON.stringify(vVal) + ")");
-}
-
-// Tests
-
-// test #1
-var example1 = new Number(3);
-alert(typeof example1); // objet
-alert(typeof emulateMessage(example1)); // nombre
-
-// test #2
-var example2 = true;
-alert(typeof example2); // booléen
-alert(typeof emulateMessage(example2)); // booléen
-
-// test #3
-var example3 = new String("Hello World");
-alert(typeof example3); // objet
-alert(typeof emulateMessage(example3)); // chaîne de caractères
-
-// test #4
-var example4 = {
-    "name": "John Smith",
-    "age": 43
-};
-alert(typeof example4); // objet
-alert(typeof emulateMessage(example4)); // objet
-
-// test #5
-function Animal (sType, nAge) {
-    this.type = sType;
-    this.age = nAge;
-}
-var example5 = new Animal("Cat", 3);
-alert(example5.constructor); // Animal
-alert(emulateMessage(example5).constructor); // Objet
- -

Une valeur qui est clonée et non partagée est appelée message. Comme vous le savez probablement dès à présent, les messages peuvent être envoyés à et à partir du thread principal en utilisant postMessage(), et l'attribut {{domxref("MessageEvent.data", "data")}} de l'événement message contient les données retournées par le worker.

- -

example.html (la 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 (leworker) :

- -
postMessage("I\'m working before postMessage(\'ali\').");
-
-onmessage = function (oEvent) {
-  postMessage("Hi " + oEvent.data);
-};
- -

L'algorithme de clonage structurée peut accepter du JSON et quelques autres choses impossibles en JSON — comme les références circulaires.

- -

Exemples de passages de données

- -

Exemple #1 : Créer un "eval() asynchrone" générique

- -

L'exemple suivant montre comment utiliser un worker afin d'exécuter de manière asynchrone n'importe quel code JavaScript permis dans un worker, au moyen d'une méthode eval() appelée dans le worker :

- -
// Syntaxe : asyncEval(code[, listener])
-
-var asyncEval = (function () {
-
-  var aListeners = [], oParser = new Worker("data:text/javascript;charset=US-ASCII,onmessage%20%3D%20function%20%28oEvent%29%20%7B%0A%09postMessage%28%7B%0A%09%09%22id%22%3A%20oEvent.data.id%2C%0A%09%09%22evaluated%22%3A%20eval%28oEvent.data.code%29%0A%09%7D%29%3B%0A%7D");
-
-  oParser.onmessage = function (oEvent) {
-    if (aListeners[oEvent.data.id]) { aListeners[oEvent.data.id](oEvent.data.evaluated); }
-    delete aListeners[oEvent.data.id];
-  };
-
-
-  return function (sCode, fListener) {
-    aListeners.push(fListener || null);
-    oParser.postMessage({
-      "id": aListeners.length - 1,
-      "code": sCode
-    });
-  };
-
-})();
- -

La data URI est équivalente à une requête réseau, avec la réponse suivante :

- -
onmessage = function (oEvent) {
-	postMessage({
-		"id": oEvent.data.id,
-		"evaluated": eval(oEvent.data.code)
-	});
-}
- -

Exemples d'utilisation :

- -
// message d'alerte asynchrone...
-asyncEval("3 + 2", function (sMessage) {
-    alert("3 + 2 = " + sMessage);
-});
-
-// affichage asynchrone d'un message...
-asyncEval("\"Hello World!!!\"", function (sHTML) {
-    document.body.appendChild(document.createTextNode(sHTML));
-});
-
-// néant asynchrone...
-asyncEval("(function () {\n\tvar oReq = new XMLHttpRequest();\n\toReq.open(\"get\", \"http://www.mozilla.org/\", false);\n\toReq.send(null);\n\treturn oReq.responseText;\n})()");
- -

Exemple #2 : passage avancé de données JSON et création d'un système d'échange

- -

Si vous devez passer des données complexes et appeler différentes fonctions à la fois dans la page principale et dans le worker, vous pouvez créer un système comme suit.

- -

example.html (la page principale) :

- -
<!doctype html>
-<html>
-<head>
-<meta charset="UTF-8"  />
-<title>MDN Example - Queryable worker</title>
-<script type="text/javascript">
-  /*
-    QueryableWorker instances methods:
-     * sendQuery(queryable function name, argument to pass 1, argument to pass 2, etc. etc): calls a Worker's queryable function
-     * postMessage(string or JSON Data): see Worker.prototype.postMessage()
-     * terminate(): terminates the Worker
-     * addListener(name, function): adds a listener
-     * removeListener(name): removes a listener
-    QueryableWorker instances properties:
-     * defaultListener: the default listener executed only when the Worker calls the postMessage() function directly
-  */
-  function QueryableWorker (sURL, fDefListener, fOnError) {
-    var oInstance = this, oWorker = new Worker(sURL), oListeners = {};
-    this.defaultListener = fDefListener || function () {};
-    oWorker.onmessage = function (oEvent) {
-      if (oEvent.data instanceof Object && oEvent.data.hasOwnProperty("vo42t30") && oEvent.data.hasOwnProperty("rnb93qh")) {
-        oListeners[oEvent.data.vo42t30].apply(oInstance, oEvent.data.rnb93qh);
-      } else {
-        this.defaultListener.call(oInstance, oEvent.data);
-      }
-    };
-    if (fOnError) { oWorker.onerror = fOnError; }
-    this.sendQuery = function (/* nom de la fonction requêtable, argument à passer 1, argument à passer 2, etc. etc */) {
-      if (arguments.length < 1) { throw new TypeError("QueryableWorker.sendQuery - not enough arguments"); return; }
-      oWorker.postMessage({ "bk4e1h0": arguments[0], "ktp3fm1": Array.prototype.slice.call(arguments, 1) });
-    };
-    this.postMessage = function (vMsg) {
-      //Je ne pense pas qu'il y ait besoin d'appeler la méthode call()
-      //que diriez-vous tout simplement de oWorker.postMessage(vMsg);
-      //le même cas se pose avec terminate
-      //bien, juste un peu plus vite, aucune recherche dans la chaîne des prototypes
-      Worker.prototype.postMessage.call(oWorker, vMsg);
-    };
-    this.terminate = function () {
-      Worker.prototype.terminate.call(oWorker);
-    };
-    this.addListener = function (sName, fListener) {
-      oListeners[sName] = fListener;
-    };
-    this.removeListener = function (sName) {
-      delete oListeners[sName];
-    };
-  };
-
-  // votre worker "queryable" personnalisé
-  var oMyTask = new QueryableWorker("my_task.js" /* , votreEcouteurDeMessageParDefautIci [optional], votreEcouteurDErreurIci [optional] */);
-
-  // vos "écouteurs" personnalisés
-
-  oMyTask.addListener("printSomething", function (nResult) {
-    document.getElementById("firstLink").parentNode.appendChild(document.createTextNode(" The difference is " + nResult + "!"));
-  });
-
-  oMyTask.addListener("alertSomething", function (nDeltaT, sUnit) {
-    alert("Worker waited for " + nDeltaT + " " + sUnit + " :-)");
-  });
-</script>
-</head>
-<body>
-  <ul>
-    <li><a id="firstLink" href="javascript:oMyTask.sendQuery('getDifference', 5, 3);">What is the difference between 5 and 3?</a></li>
-    <li><a href="javascript:oMyTask.sendQuery('waitSomething');">Wait 3 seconds</a></li>
-    <li><a href="javascript:oMyTask.terminate();">terminate() the Worker</a></li>
-  </ul>
-</body>
-</html>
- -

my_task.js (le worker) :

- -
// vos fonctions PRIVEES personnalisées
-
-function myPrivateFunc1 () {
-  // instructions à exécuter
-}
-
-function myPrivateFunc2 () {
-  // instructions à exécuter
-}
-
-// etc. etc.
-
-// vos fonctions PUBLIQUES personnalisées (i.e. requêtables depuis la page principale)
-
-var queryableFunctions = {
-  // exemple #1 : obtenir la différence entre deux nombres :
-  getDifference: function (nMinuend, nSubtrahend) {
-      reply("printSomething", nMinuend - nSubtrahend);
-  },
-  // exemple #2 : attendre trois secondes
-  waitSomething: function () {
-      setTimeout(function() { reply("alertSomething", 3, "seconds"); }, 3000);
-  }
-};
-
-// fonctions système
-
-function defaultQuery (vMsg) {
-  // votre fonction PUBLIQUE par défaut est exécutée seulement lorsque la page principale appelle la méthode queryableWorker.postMessage() directement
-  // instructions à exécuter
-}
-
-function reply (/* listener name, argument to pass 1, argument to pass 2, etc. etc */) {
-  if (arguments.length < 1) { throw new TypeError("reply - not enough arguments"); return; }
-  postMessage({ "vo42t30": arguments[0], "rnb93qh": Array.prototype.slice.call(arguments, 1) });
-}
-
-onmessage = function (oEvent) {
-  if (oEvent.data instanceof Object && oEvent.data.hasOwnProperty("bk4e1h0") && oEvent.data.hasOwnProperty("ktp3fm1")) {
-    queryableFunctions[oEvent.data.bk4e1h0].apply(self, oEvent.data.ktp3fm1);
-  } else {
-    defaultQuery(oEvent.data);
-  }
-};
- -

Il est possible d'échanger le contenu de chaque message page principale -> worker et worker -> page principale.

- -

Passage de données par transfert de propriété (objets transférables)

- -

Google Chrome 17+ et Firefox 18+ proposent une manière additionnelle de passer certains types d'objets (les objets transférables, c'est-à-dire les objets implémentant l'interface {{domxref("Transferable")}}) vers ou à partir d'un worker avec une haute performance. Les objets transférables sont transférés d'un contexte vers un autre sans aucune opération de copie, ce qui conduit à d'énormes gains de performance lorsque de gros ensembles de données sont envoyés. Considérez la chose comme un passage par référence si vous venez du monde C/C++. Cependant, contrairement au passage par référence, la 'version' issue du contexte appelant n'est plus disponible une fois transférée. Sa propriété est transférée au nouveau contexte. Par exemple, lors du transfert d'un {{domxref("ArrayBuffer")}} à partir de votre application principale vers le script d'un worker, le {{domxref("ArrayBuffer")}} original est nettoyé et définitivement inutilisable. Son contenu est (tout à fait littéralement) transféré au contexte du worker.

- -
// Crée un "fichier" de 32MB et le remplit.
-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]);
-
- -
-

Remarque : pour plus d'information sur les objets transférables, la performance et la détection de fonctionnalité de cette méthode, lisez Transferable Objects: Lightning Fast! sur HTML5 Rocks.

-
- -

Workers embarqués

- -

Il n'y a pas une manière "officielle" d'embarquer le code d'un worker dans une page web, comme les éléments {{ HTMLElement("script") }} le font pour les scripts normaux. Mais un élément {{ HTMLElement("script") }} qui n'aurait pas d'attribut src et dont l'attribut type n'identifierait pas un type MIME exécutable peut être considéré comme un élément de bloc de données dont JavaScript peut faire usage.  Les "blocs de données" sont une caractéristique plus générale d'HTML5 qui peuvent contenir presque n'importe quelles données textuelles. Ainsi, un worker pourrait être embarqué de cette façon :

- -
<!DOCTYPE html>
-<html>
-<head>
-<meta charset="UTF-8" />
-<title>MDN Example - Embedded worker</title>
-<script type="text/js-worker">
-  // Ce script NE SERA PAS traité par les moteurs JS parce que son type MIME est text/js-worker.
-  var myVar = "Hello World!";
-  // Le reste du code de votre worker commence ici.
-</script>
-<script type="text/javascript">
-  // Ce script SERA analysé par les moteurs JS engines parce que son type MIME est text/javascript.
-  function pageLog (sMsg) {
-    // Utilisation d'un fragment : le navigateur réaffichera/réorganisera le DOM seulement une 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 traité par les moteurs JS parce que son type MIME est text/js-worker.
-  onmessage = function (oEvent) {
-    postMessage(myVar);
-  };
-  // Le reste du code de votre worker commence ici.
-</script>
-<script type="text/javascript">
-  // Ce script SERA analysé par les moteurs JS engines parce que son type MIME est text/javascript.
-
-  // Dans le passé... :
-  // blob builder a existé
-  // ...mais nous utilisons désormais Blob...:
-  var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});
-
-  // Création d'une nouvelle propriété document.worker contenant tous nos scripts "text/js-worker".
-  document.worker = new Worker(window.URL.createObjectURL(blob));
-
-  document.worker.onmessage = function (oEvent) {
-    pageLog("Received: " + oEvent.data);
-  };
-
-  // Démarrage du worker.
-  window.onload = function() { document.worker.postMessage(""); };
-</script>
-</head>
-<body><div id="logDisplay"></div></body>
-</html>
- -

Le worker embarqué est maintenant imbriqué dans une nouvelle propriété personnalisée document.worker.

- -

Exemples

- -

Cette section fournit plusieurs exemples sur la façon d'utiliser les workers DOM.

- -

Réaliser des calculs en arrière-plan

- -

Les workers sont principalement utiles pour permettre à votre code de réaliser des calculs très consommateur en CPU sans bloquer le thread de l'interface utilisateur. Dans cet exemple, un worker est utilisé pour calculer la suite de Fibonacci.

- -

Le code JavaScript

- -

Le code JavaScript suivant est stocké dans le fichier "fibonacci.js" référencé par le fichier HTML dans la prochaine section.

- -
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);
-  }
- };
- -

Le worker affecte à la propriété onmessage  une fonction qui recevra les messages envoyés lorsque la méthode  postMessage() de l'objet worker est appelée (remarquez que cela diffère de définir une variable globale de ce nom, ou de définir une fonction avec ce nom.   var onmessage et function onmessage définissent des propriétés globales avec ces noms, mais elles n'enregistrent pas la fonction pour recevoir les messages envoyés par la page web qui a créé le worker). Au démarrage de la récursion, il engendre ainsi de nouvelles copies de lui-même pour gérer chacune des itérations du calcul.

- -

Le code HTML

- -
<!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) {
-      dump("Worker error: " + error.message + "\n");
-      throw error;
-    };
-
-    worker.postMessage("5");
-
-  </script>
-  </body>
-</html>
-
- -

La page web crée un élément div avec l'ID  result , qui sera utilisé pour afficher le résultat, puis engendre le worker.  Après création du worker, le gestionnaire onmessage est configuré pour afficher les résultats en renseignant le contenu de l'élément div, et le gestionnaire onerror est configuré pour capturer le message d'erreur.

- -

Finalement, un message est envoyé au worker pour le démarrer.

- -

Tester cet exemple.

- -

Réaliser des E/S web en arrière-plan

- -

Vous pouvez trouver un tel exemple dans l'article Using workers in extensions .

- -

Répartir des tâches entre plusieurs workers

- -

Les ordinateurs multi-coeur étant de plus en plus répandus, il est souvent utile de répartir le calcul de tâches complexes entre différents workers afin de tirer partie des coeurs de ces multiprocesseurs.

- -

Voir aussi

- - diff --git a/files/fr/web/api/web_workers_api/algorithme_clonage_structure/index.html b/files/fr/web/api/web_workers_api/algorithme_clonage_structure/index.html deleted file mode 100644 index dfb8c20bf9..0000000000 --- a/files/fr/web/api/web_workers_api/algorithme_clonage_structure/index.html +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: L’algorithme de clonage structuré -slug: Web/API/Web_Workers_API/algorithme_clonage_structure -translation_of: Web/API/Web_Workers_API/Structured_clone_algorithm ---- -

L’algorithme de clonage structuré est un nouvel algorithme défini par la spécification HTML5 pour sérialiser les objets JavaScript complexes. Il est plus puissant que JSON en cela qu’il supporte la sérialisation d’objets contenant des graphes cycliques — des objets peuvent faire référence à des objets faisant référence à d’autres objets dans le même graphe. De plus, dans certains cas, l’algorithme de clonage structuré peut être plus efficace que JSON.

- -

L’algorithme, essentiellement, parcourt tous les champs de l’objet original, copiant les valeurs de chaque champ dans un nouvel objet. Si un champ est lui-même un objet avec des champs, ces champs sont parcourus de manière récursive jusqu’à ce que chaque champ et sous-champ aient été copié dans le nouvel objet.

- -

Avantages par rapport à JSON

- -

Il y a quelques avantages notables à utiliser l’algorithme de clonage structuré plutôt que JSON :

- - - -

Ce qui ne marche pas avec le clonage structuré

- - - -

Types supportés

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Type d’objetNotes
Tous types primitifsÀ l’exception des symboles
Objet Booléen 
Objet String 
Date 
RegExpLe champ lastIndex n’est pas préservé
{{ domxref("Blob") }} 
{{ domxref("File") }} 
{{ domxref("FileList") }} 
ArrayBuffer 
ArrayBufferViewCe qui implique tous les tableaux typés tels que Int32Array, etc.
{{ domxref("ImageData") }} 
Array 
ObjectInclut seulement les objets plats (par ex. depuis un objet littéral)
Map 
Set 
- -

Alternative : copie profonde

- -

Si vous voulez une copie profonde d’un objet (c’est-à-dire une copie récursive de toutes les propriétés imbriquées, en parcourant la chaîne des prototypes), vous devez employer une autre approche. Ce qui suit est un exemple possible.

- -
function clone(objectToBeCloned) {
-  // Cas basique.
-  if (!(objectToBeCloned instanceof Object)) {
-    return objectToBeCloned;
-  }
-
-  var objectClone;
-
-  // Filtre les objets spéciaux.
-  var Constructor = objectToBeCloned.constructor;
-  switch (Constructor) {
-    // Implémenter d’autres objets spéciaux ici.
-    case RegExp:
-      objectClone = new Constructor(objectToBeCloned);
-      break;
-    case Date:
-      objectClone = new Constructor(objectToBeCloned.getTime());
-      break;
-    default:
-      objectClone = new Constructor();
-  }
-
-  // Clone chaque propriété.
-  for (var prop in objectToBeCloned) {
-    objectClone[prop] = clone(objectToBeCloned[prop]);
-  }
-
-  return objectClone;
-}
-
- -
  Note : Cet algorithme ne prend en charge que les objets spéciaux RegExp, Array et Date. Vous pouvez implémenter d’autres cas spéciaux selon vos besoins.
- -

Voir aussi

- - diff --git a/files/fr/web/api/web_workers_api/functions_and_classes_available_to_workers/index.html b/files/fr/web/api/web_workers_api/functions_and_classes_available_to_workers/index.html new file mode 100644 index 0000000000..379f86edd6 --- /dev/null +++ b/files/fr/web/api/web_workers_api/functions_and_classes_available_to_workers/index.html @@ -0,0 +1,240 @@ +--- +title: Fonctions et classes disponibles dans les Web Workers +slug: Web/API/Worker/Functions_and_classes_available_to_workers +translation_of: Web/API/Web_Workers_API/Functions_and_classes_available_to_workers +--- +

En plus de l'ensemble des fonctions standard JavaScript (telles que {{jsxref("Global_Objects/String", "String")}}, {{jsxref("Global_Objects/Array", "Array")}}, {{jsxref("Global_Objects/Object", "Object")}}, {{jsxref("Global_Objects/JSON", "JSON")}} etc), des fonctions du DOM restent disponibles pour les workers. Cet article en fournit la liste.

+ +

Les workers s'exécutent dans un contexte global, {{domxref("DedicatedWorkerGlobalScope")}} différent du contexte de la fenêtre courante. Par défaut les méthodes et propriétés de {{domxref("Window")}} ne leur sont pas disponibles, mais {{domxref("DedicatedWorkerGlobalScope")}}, comme Window, implémente {{domxref("WindowTimers")}} et {{domxref("WindowBase64")}}.

+ +

Comparaison des propriétés et méthodes des différents types de workers

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FonctionsWorkers dédiésWorkers partagésService workersChrome workers {{Non-standard_inline}}En dehors des  workers
{{domxref("WindowBase64.atob", "atob()")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("Window")}}
{{domxref("WindowBase64.btoa", "btoa()")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("Window")}}
{{domxref("WindowTimers.clearInterval", "clearInterval()")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("Window")}}
{{domxref("WindowTimers.clearTimeout", "clearTimeout()")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("Window")}}
{{domxref("Window.dump()", "dump()")}} {{non-standard_inline}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("Window")}}
{{domxref("WindowTimers.setInterval", "setInterval()")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("Window")}}
{{domxref("WindowTimers.setTimeout", "setTimeout()")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("Window")}}
{{domxref("WorkerGlobalScope.importScripts", "importScripts()")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}non
{{domxref("WorkerGlobalScope.close", "close()")}} {{non-standard_inline}}oui, sur {{domxref("WorkerGlobalScope")}}oui, sur {{domxref("WorkerGlobalScope")}}oui, mais sans effetInconnunon
{{domxref("DedicatedWorkerGlobalScope.postMessage", "postMessage()")}}oui, sur {{domxref("DedicatedWorkerGlobalScope")}}nonnonInconnunon
+ +

APIs disponibles dans les workers

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FonctionFonctionnalitéSupport dans Gecko (Firefox)Support dans IESupport dans Blink (Chrome et Opera)Support dans WebKit (Safari)
XMLHttpRequestCrée et retourne un nouvel objet {{domxref("XMLHttpRequest")}}; il imite le comportement du constructeur standard XMLHttpRequest(). Remarquez que les attributs responseXML et channel de XMLHttpRequest retourne toujours null. +

Basique : {{CompatGeckoDesktop("1.9.1")}}

+ +

{{domxref("XMLHttpRequest.response", "response")}} et {{domxref("XMLHttpRequest.responseType", "responseType")}} sont disponibles depuis {{CompatGeckoDesktop("10")}}

+ +

{{domxref("XMLHttpRequest.timeout", "timeout")}} et {{domxref("XMLHttpRequest.ontimeout", "ontimeout")}} sont disponibles depuis {{CompatGeckoDesktop("13")}}

+
{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}
WorkerCrée un nouveau {{ domxref("Worker") }}. Oui, les workers peuvent engendrer des workers supplémentaires.{{CompatGeckoDesktop("1.9.1")}}10.0{{CompatNo}} Voir crbug.com/31666{{CompatNo}}
{{ domxref("URL") }}Les workers peuvent utiliser les méthodes statiques URL.createObjectURL et URL.revokeObjectURL avec les objets {{domxref("Blob")}} accessibles au worker.
+ Les workers peuvent aussi créer une nouvelle URL en utilisant le constructeur {{domxref("URL.URL", "URL()")}} et appeler n'importe quelle méthode normale sur l'objet retourné.
{{CompatGeckoDesktop(21)}} et {{CompatGeckoDesktop(26)}} pour le constructeur URL(){{CompatNo}}{{CompatNo}}{{CompatNo}}
{{domxref("TextEncoder")}} and {{domxref("TextDecoder")}}Crée et retourne un nouveau {{domxref("TextEncoder")}}, ou respectivement {{domxref("TextDecoder")}}, permettant d'encoder ou de décoder des chaînes de caractère dans un encodage spécifique.{{CompatGeckoDesktop(20)}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
{{domxref("WorkerNavigator")}}Le sous-ensemble de l'interface {{domxref("Navigator")}} disponible aux workers.Implémentation basique {{CompatVersionUnknown}}
+ {{domxref("NavigatorID.appCodeName", "appCodeName")}}, {{domxref("NavigatorID.product", "product")}}, {{domxref("NavigatorID.taintEnabled", "taintEnabled()")}}: {{CompatGeckoDesktop(28)}}
+ {{domxref("WorkerNavigator.onLine", "onLine")}}: {{CompatGeckoDesktop(29)}}
+ {{domxref("NavigatorLanguage")}}: {{CompatNo}}
{{domxref("NavigatorID.appName", "appName")}}, {{domxref("NavigatorID.appVersion", "appName")}}, {{domxref("WorkerNavigator.onLine", "onLine")}}, {{domxref("NavigatorID.platform", "platform")}}, {{domxref("NavigatorID.userAgent", "userAgent")}}: 10.0
+ Autre : {{CompatNo}}
{{CompatVersionUnknown}}{{CompatVersionUnknown}}
{{domxref("WorkerLocation")}}Le sous-ensemble de l'interface {{domxref("Location")}} disponible aux workers.{{CompatGeckoDesktop(1.9.2)}}10.0{{CompatVersionUnknown}}{{CompatVersionUnknown}}
{{domxref("WorkerGlobalScope")}}Le contexte global des workers. Cet objet définit les fonctions spécifiques aux workers.{{CompatVersionUnknown}}10.0{{CompatVersionUnknown}}{{CompatVersionUnknown}}
{{domxref("ImageData")}}Les données en pixels sous-jacentes à une zone d'un élément {{domxref("canvas")}}. Manipuler de telles données peut être une tâche complexe qu'il est plus approprié de déléguer à un web worker.{{CompatGeckoDesktop(25)}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
{{domxref("FileReaderSync")}}Cette API permet la lecture synchrone d'objets {{domxref("Blob")}} et {{domxref("File")}}. C'est une API qui fonctionne uniquement au sein des workers.{{CompatGeckoDesktop(8)}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
{{domxref("IndexedDB_API", "IndexedDB")}}Une base de données pour stocker des enregistrements contenant des valeurs simples et des objets hiérarchiques.{{CompatGeckoDesktop(37)}}10.0{{CompatVersionUnknown}}{{CompatNo}}
{{domxref("WebSocket")}}Crée et retourne un nouvel objet {{domxref("WebSocket")}}; Il imite le comportement d'un constructeur WebSocket() standard.{{CompatGeckoDesktop(36)}}11.0{{CompatVersionUnknown}}{{CompatVersionUnknown}}
Data Store APIUn mécanisme de stockage puissant et flexible pour de multiples applications Firefox OS qui ont l'habitude de stocker et d'échanger des données entre elles de manière rapide, efficace et sécurisée.Seulement dans les applications internes (certifiées) de Firefox OS, deuis v1.0.1.{{CompatNo}}{{CompatNo}}{{CompatNo}}
PromisesLes objets JavaScript qui vous permettent d'écrire des fonctions asynchrones.{{CompatGeckoDesktop(28)}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}
+ +

Voir aussi

+ + diff --git a/files/fr/web/api/web_workers_api/structured_clone_algorithm/index.html b/files/fr/web/api/web_workers_api/structured_clone_algorithm/index.html new file mode 100644 index 0000000000..dfb8c20bf9 --- /dev/null +++ b/files/fr/web/api/web_workers_api/structured_clone_algorithm/index.html @@ -0,0 +1,153 @@ +--- +title: L’algorithme de clonage structuré +slug: Web/API/Web_Workers_API/algorithme_clonage_structure +translation_of: Web/API/Web_Workers_API/Structured_clone_algorithm +--- +

L’algorithme de clonage structuré est un nouvel algorithme défini par la spécification HTML5 pour sérialiser les objets JavaScript complexes. Il est plus puissant que JSON en cela qu’il supporte la sérialisation d’objets contenant des graphes cycliques — des objets peuvent faire référence à des objets faisant référence à d’autres objets dans le même graphe. De plus, dans certains cas, l’algorithme de clonage structuré peut être plus efficace que JSON.

+ +

L’algorithme, essentiellement, parcourt tous les champs de l’objet original, copiant les valeurs de chaque champ dans un nouvel objet. Si un champ est lui-même un objet avec des champs, ces champs sont parcourus de manière récursive jusqu’à ce que chaque champ et sous-champ aient été copié dans le nouvel objet.

+ +

Avantages par rapport à JSON

+ +

Il y a quelques avantages notables à utiliser l’algorithme de clonage structuré plutôt que JSON :

+ + + +

Ce qui ne marche pas avec le clonage structuré

+ + + +

Types supportés

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type d’objetNotes
Tous types primitifsÀ l’exception des symboles
Objet Booléen 
Objet String 
Date 
RegExpLe champ lastIndex n’est pas préservé
{{ domxref("Blob") }} 
{{ domxref("File") }} 
{{ domxref("FileList") }} 
ArrayBuffer 
ArrayBufferViewCe qui implique tous les tableaux typés tels que Int32Array, etc.
{{ domxref("ImageData") }} 
Array 
ObjectInclut seulement les objets plats (par ex. depuis un objet littéral)
Map 
Set 
+ +

Alternative : copie profonde

+ +

Si vous voulez une copie profonde d’un objet (c’est-à-dire une copie récursive de toutes les propriétés imbriquées, en parcourant la chaîne des prototypes), vous devez employer une autre approche. Ce qui suit est un exemple possible.

+ +
function clone(objectToBeCloned) {
+  // Cas basique.
+  if (!(objectToBeCloned instanceof Object)) {
+    return objectToBeCloned;
+  }
+
+  var objectClone;
+
+  // Filtre les objets spéciaux.
+  var Constructor = objectToBeCloned.constructor;
+  switch (Constructor) {
+    // Implémenter d’autres objets spéciaux ici.
+    case RegExp:
+      objectClone = new Constructor(objectToBeCloned);
+      break;
+    case Date:
+      objectClone = new Constructor(objectToBeCloned.getTime());
+      break;
+    default:
+      objectClone = new Constructor();
+  }
+
+  // Clone chaque propriété.
+  for (var prop in objectToBeCloned) {
+    objectClone[prop] = clone(objectToBeCloned[prop]);
+  }
+
+  return objectClone;
+}
+
+ +
  Note : Cet algorithme ne prend en charge que les objets spéciaux RegExp, Array et Date. Vous pouvez implémenter d’autres cas spéciaux selon vos besoins.
+ +

Voir aussi

+ + diff --git a/files/fr/web/api/web_workers_api/using_web_workers/index.html b/files/fr/web/api/web_workers_api/using_web_workers/index.html new file mode 100644 index 0000000000..b1330f34bf --- /dev/null +++ b/files/fr/web/api/web_workers_api/using_web_workers/index.html @@ -0,0 +1,633 @@ +--- +title: Utilisation des web workers +slug: Web/API/Web_Workers_API/Utilisation_des_web_workers +tags: + - Avancé + - Guide + - JavaScript + - Web Workers +translation_of: Web/API/Web_Workers_API/Using_web_workers +--- +
{{DefaultAPISidebar("Web Workers API")}}
+ +

Les Web Workers sont un outil permettant au contenu web d'exécuter des scripts dans des tâches (threads) d'arrière-plan. Le thread associé au worker peut réaliser des tâches sans qu'il y ait d'interférence avec l'interface utilisateur. De plus, les web workers peuvent réaliser des opérations d'entrée/sortie grâce à XMLHttpRequest (bien que les attributs responseXML et channel soient nécessairement vides dans ces cas). Une fois créé, un worker peut envoyer des messages au code JavaScript qui l'a créé. De même, le script initial peut envoyer des messages au worker. Cette communication s'effectue grâce à des gestionnaires d'évènements. Dans cet article, nous verrons une introduction à l'utilisation des web workers.

+ +

L'API Web Workers

+ +

Un worker est un objet créé à l'aide d'un constructeur (par exemple {{domxref("Worker.Worker", "Worker()")}}) et qui exécute un fichier JavaScript donné. Ce fichier contient le code qui sera exécuté par le thread du worker. Les workers sont exécutés dans un contexte global qui n'est pas celui du document (généralement {{domxref("window")}}). Aussi, si, dans un worker, on utilise {{domxref("window")}} pour accéder à la portée globale (plutôt que {{domxref("window.self","self")}}), cela provoquera une erreur.

+ +

Le contexte du worker est représenté par un objet {{domxref("DedicatedWorkerGlobalScope")}} pour les workers dédiés et par un objet {{domxref("SharedWorkerGlobalScope")}} sinon. Un worker dédié est uniquement accessible au travers du script qui l'a déclenché tandis qu'un worker partagé peut être utilisé par différents scripts.

+ +
+

Note : Voir la page d'entrée pour l'API Web Workers pour consulter la documentation de référence sur les workers et d'autres guides.

+
+ +

Il est possible d'exécuter n'importe quel code JavaScript dans le thread du worker, à l'exception des méthodes de manipulation du DOM ou de certaines propriétés et méthodes rattachées à {{domxref("window")}}. On notera cependant qu'on peut tout à fait utiliser certaines API rendues disponibles via window comme les WebSockets, les API de stockage de données telles que IndexedDB. Pour plus de détails, voir les fonctions et classes disponibles au sein des workers.

+ +

Les données sont échangées entre le thread du worker et le thread principal par l'intermédiaire 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 (le message sera contenu dans l'attribut data de l'évènement {{event("Message")}} associé). Les données sont copiées dans le message, elles ne sont pas partagées.

+ +

Les workers peuvent également déclencher la création d'autres workers tant que ceux-ci restent hébergés sur la même origine que la page parente. De plus, les workers pourront utiliser XMLHttpRequest pour effectuer des opérations réseau mais les attributs responseXML et channel de XMLHttpRequest renverront nécessairement null.

+ +

Les workers dédiés

+ +

Comme indiqué plus haut, un worker dédié 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 dédié simple. Dans cet exemple, nous souhaitons multiplier deux nombres. Ces nombres sont envoyés à un worker dédié puis le résultat 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 détails plus avancés dans la suite de cet article.

+ +

Détecter la possibilité d'utiliser les workers

+ +

Afin de gérer une meilleure amélioration progressive, une rétro-compatibilité et de présenter des messages d'erreur adéquats, il pourra être utile d'envelopper le code relatif au worker de la façon suivante (main.js) :

+ +
if (window.Worker) {
+  ...
+}
+ +

Initier un worker dédié

+ +

La création d'un nouveau worker est assez simple. On appellera le constructeur {{domxref("Worker.Worker", "Worker()")}} en indiquant l'URI du script à exécuter dans le thread associé au worker (main.js) :

+ +
+
var monWorker = new Worker('worker.js');
+
+
+ +

Envoyer des messages au worker et y réagir

+ +

L'intérêt principal des workers repose sur l'échange de messages à l'aide de la méthode {{domxref("Worker.postMessage", "postMessage()")}} et grâce au gestionnaire d'évènement {{domxref("Worker.onmessage", "onmessage")}}. Lorsqu'on souhaite envoyer un message au worker, on enverra des messages de la façon 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 éléments {{htmlelement("input")}} représentés 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 utilisés pour échanger n'importe quel type de valeur.

+ +

Dans le worker, on peut réagir au message reçu grâce à un gestionnaire d'évènement comme celui-ci (worker.js) :

+ +
onmessage = function(e) {
+  console.log('Message reçu depuis le script principal.');
+  var workerResult = 'Résultat : ' + (e.data[0] * e.data[1]);
+  console.log('Envoi du message de retour au script principal');
+  postMessage(workerResult);
+}
+ +

Le gestionnaire onmessage permet d'exécuter du code lorsqu'un message est reçu. Le message même est disponible grâce à l'attribut data de l'évènement. Dans cet exemple, nous multiplions simplement les deux nombres avant d'utiliser postMessage() à nouveau afin d'envoyer le résultat via un message destiné au thread principal.

+ +

De retour dans le thread principal, nous pouvons utiliser onmessage à nouveau pour réagir à la réponse provenant du worker :

+ +
monWorker.onmessage = function(e) {
+  resultat.textContent = e.data;
+  console.log('Message reçu depuis le worker');
+}
+ +

Ici, nous récupérons les données grâce à l'attribut data de l'évènement et nous mettons à jour le contenu du paragraphe avec l'attribut textContent de l'élément. Ainsi, l'utilisateur peut visualiser le résultat du calcul.

+ +
Note : On notera que onmessage et postMessage() doivent être rattachés à un objet Worker lorsqu'ils sont utilisés depuis le thread principal (ici, c'était monWorker) mais pas lorsqu'ils sont employés depuis le worker. En effet, dans le worker, c'est le worker qui constitue la portée globale et qui met à disposition ces méthodes.
+ +
Note : Lorsqu'un message est envoyé d'un thread à l'autre, ses données sont copiées. Elles ne sont pas partagées. Voir ci-après pour plus d'explications à ce sujet.
+ +

Clôturer un worker

+ +

Si on doit arrêter un worker immédiatement, on pourra utiliser la méthode {{domxref("Worker", "terminate")}} depuis le thread principal :

+ +
monWorker.terminate();
+ +

Lorsque cette méthode exécuté, le thread associé au worker est tué immédiatement.

+ +

Gérer les erreurs

+ +

Lorsqu'une erreur d'exécution se produit avec le worker, son gestionnaire d'évènement onerror est appelé et reçoit un évènement error qui implémente l'interface ErrorEvent.

+ +

Cet évènement ne bouillonne (bubble) pas et peut être annulé. Pour empêcher les conséquences par défaut, on pourra utiliser la méthode preventDefault() rattachée à l'évènement d'erreur.

+ +

L'évènement décrivant l'erreur possède notamment trois propriétés intéressantes :

+ +
+
message
+
Un message d'erreur compréhensible par un humain.
+
filename
+
Le nom du fichier pour lequel l'erreur s'est produite.
+
lineno
+
Le numéro de ligne au sein du fichier responsable de l'erreur.
+
+ +

Initier des workers fils

+ +

Les workers peuvent également engendrer d'autres workers. Ces workers-fils doivent être hébergés sur la même origine que la page initiale. De plus, les URI des workers-fils sont résolues relativement à l'emplacement du worker père (plutôt que par rapport à la page parente). Ces contraintes permettent de simplifier le suivi des dépendances.

+ +

Importer des scripts et des bibliothèques

+ +

Les threads d'exécution des workers peuvent accéder à la fonction globale importScripts(), qui leur permet d'importer des scripts. Cette fonction prend zéro à plusieurs URL en paramètres et importe les ressources associées. 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'exécutera. Chaque script pourra ainsi mettre à disposition des objets globaux qui pourront être utilisés par le worker. Si le script ne peut pas être chargé, une exception NETWORK_ERROR sera levée et le code assicé ne sera pas exécuté. Le code exécuté précédemment (y compris celui-ci reporté à l'aide de {{domxref("window.setTimeout()")}}) continuera cependant d'être fonctionnel. Les déclarations de fonction situées après importScripts() sont également exécutées car évaluées avant le reste du code.

+ +
Note : Les scripts peuvent être téléchargés dans n'importe quel ordre mais ils seront exécutés dans l'ordre des arguments passés à importScripts() . Cet exécution est effectuée de façon synchrone et importScripts() ne rendra pas la main tant que l'ensemble des scripts n'auront pas été chargés et exécutés.
+ +

Les workers partagés

+ +

Un worker partagé est accessible par plusieurs scripts (même si ceux-ci proviennent de différentes fenêtres, 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 dédié. Il diffère car il possède deux fonctions, gérées 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 même worker pour réaliser le calcul demandé.

+ +

Ici, nous nous intéresserons particulièrement aux différences entre les workers dédiés et les workers partagés. Dans cet exemple, nous aurons deux pages HTML, chacune utilisant du code JavaScript employant le même worker.

+ +
+

Note : Si on peut accéder à un worker partagé depuis différents contextes de navigations, ces contextes de navigation doivent néanmoins partager la même origine (même protocole, même hôte, même port).

+
+ +
+

Note : Dans Firefox, les workers partagés ne peuvent pas être partagés entre les documents chargés en navigation privée et les documents chargés en navigation classique ({{bug(1177621)}}).

+
+ +

Initier un worker partagé

+ +

La création d'un nouveau worker partagé est assez semblable à la création d'un worker dédié. On utilise alors un constructeur différent :

+ +
var monWorker = new SharedWorker('worker.js');
+ +

Une différence fondamentale avec les workers dédiés 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 dédiés, ce port est ouvert implicitement).

+ +

La connexion au port doit être démarrée implicitement avec l'utilisation du gestionnaire d'évènement onmessage ou explicitement avec la méthode start() avant qu'un message soit envoyé. On utilisera uniquement start() si l'évènement message est détecté avec la méthode addEventListener().

+ +
+

Note : Lorsqu'on utilise la méthode start() afin d'ouvrir le port de connexion, celle-ci doit être appelée de part et d'autre (depuis le thread parent et depuis le worker) si on souhaite disposer d'une connexion bidirectionnelle.

+
+ +

Échanger des messages avec un worker partagé et y réagir

+ +

On peut alors envoyer des messages au worker. Dans le cas d'un worker partagé, la méthode postMessage() doit être appelée 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 côté du worker, les choses sont également légèrement plus compliquées (voir worker.js) :

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

On commence par utiliser le gestionnaire onconnect afin de déclencher du code à la connexion au port (c'est-à-dire lorsque le gestionnaire onmessage est déclaré depuis le thread parent ou lorsque la méthode start() est invoquée explicitement depuis le thread parent).

+ +

On utilise l'attribut ports de l'évènement afin de récupérer le port utilisé et on le place dans une variable.

+ +

Ensuite, sur ce port, on ajoute un gestionnaire d'évènement pour l'évènement message afin de faire les calculs et de renvoyer le résultat au thread principal. En définissant 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 nécessaire d'invoquer port.start().

+ +

Enfin, dans le script de la page, on gère le message du résultat (voir multiply.js et square.js):

+ +
monWorker.port.onmessage = function(e) {
+  result2.textContent = e.data;
+  console.log('Message reçu depuis le worker');
+}
+ +

Lorsqu'un message provient du port associé au worker, on vérifie son type puis on insère le résultat dans le paragraphe associé.

+ +

Sûreté des threads

+ +

L'interface {{domxref("Worker")}} engendre des threads au sens du système d'exploitation. Certains développeurs avertis pourront se demander si cette communication à base de threads ne peut pas générer d'effets indésirables tels que des situations de compétition (race conditions).

+ +

Toutefois, la communication entre les web workers est contrôlée explicitement dans les scripts et il n'y a pas d'accès aux composants ou au DOM qui ne seraient pas sûrs à ce niveau. De plus, la communication entre les threads s'effectue par recopie de données. Aussi, s'il n'est théoriquement pas impossible de ne pas avoir de tels problèmes, il faudrait les chercher pour les provoquer.

+ +

Règles de sécurité du contenu (content security policy, CSP)

+ +

Les workers disposent de leur propre contexte d'exécution, distinct de celui du document qui les a créés. Aussi, en général, les workers ne sont pas gérés par la politique de sécurité de contenu du document (ou du worker parent) responsable de leur création. Ainsi, si un document est servi avec l'en-tête suivant :

+ +
Content-Security-Policy: script-src 'self'
+ +
Cette règle empêchera n'importe quel script inclus dans le document d'utiliser eval(). Toutefois, si le script génère un worker, le code exécuté par ce worker pourra utiliser eval().
+
+Pour appliquer une règle de sécurité au worker, il faudra fournir un en-tête Content-Security-Policy approprié pour la requête responsable du service du script du worker.
+
+Si l'origine du script du worker est un identifiant global unique (si son URL utilise le schéma data:// ou blob:// par exemple), le worker héritera du CSP associé au document responsable de sa création.
+ +
+ +

Échanger des données avec les workers : plus de détails

+ +

Les données échangées entre le document principal et les workers sont copiées et non partagées. Lorsqu'ils sont envoyés au worker, les objets sont sérialisés (puis désérialisés à leur réception). La page et le worker ne partagent pas le même exemplaire et on a donc deux versions d'une part et d'autre. La plupart des navigateurs implémentent cette approche avec une clonage structurel.

+ +

Pour illustrer ce point, on prendra une fonction intitulée emulateMessage() et qui simule le comportement d'une valeur clonée (pas partagée) avec un worker attaché à la page principale et réciproquement :

+ +
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 clonée et non partagée est appelée un message. Les messages peuvent être envoyés et reçus grâce à postMessage() et au gestionnaire d'évènement pour message (dont l'attribut {{domxref("MessageEvent.data", "data")}} contiendra les données copiées).

+ +

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 sérialiser aussi bien des données JSON que d'autres formats et permet notamment de gérer les références circulaires (ce que JSON ne permet pas de gérer nativement).

+ +

Les objets transférables - échanger des données avec transfert de la propriété

+ +

Chrome 17+ et Firefox 18+ permettent également d'échanger certains types d'objet (qualifiés de transférables et qui implémentent l'interface {{domxref("Transferable")}}) avec des workers et à haute performance. Les objets transférables sont passés d'un contexte à l'autre avec une opération zero-copy qui permet d'obtenir des améliorations sensibles lors de l'échange de données volumineuses. On peut voir cela comme un passage de référence du monde C/C++ mais les données qui sont transférées depuis le contexte appelant ne sont plus disponibles dans ce contexte après le transfert. La propriété des données est transférée au nouveau contexte. Ainsi, lorsqu'on transfère un objet {{domxref("ArrayBuffer")}} depuis l'application principale vers le worker, l'objet {{domxref("ArrayBuffer")}} de départ est nettoyé et ne peut plus être utilisé, son contenu est (littéralement) transféré au contexte du worker.

+ +
// Créer 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 transférables, aux performances associées et à la détection de ces fonctionnalités, on pourra lire Transferable Objects: Lightning Fast.

+
+ +

Workers embarqués

+ +

Il n'existe pas de méthode standard pour embarquer le code d'un worker dans une page web à la façon des éléments {{HTMLElement("script")}}. Toutefois, un élément {{HTMLElement("script")}}, qui ne possède pas d'attribut src, qui possède un attribut type ne correspondant pas à un type MIME exécutable pourra être considéré comme un bloc de données pouvant être utilisé par JavaScript. Ces blocs de données sont une fonctionnalité HTML5 qui permet de transporter n'importe quelle donnée textuelle. On pourrait donc embarquer un worker de cette façon :

+ +
<!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 crée une nouvelle propriété 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 démarre le worker.
+  window.onload = function() { document.worker.postMessage(''); };
+</script>
+</head>
+<body><div id="logDisplay"></div></body>
+</html>
+ +

Le worker embarqué est désormais injecté dans la propriété document.worker.

+ +

On notera également qu'on peut convertir une fonction en un Blob et générer 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)
+}
+ +

Autres exemples

+ +

Dans cette section nous voyons d'autres exemples d'application.

+ +

Effectuer des calculs en arrière-plan

+ +

Les workers sont notamment utiles pour réaliser des opérations 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.

+ +

JavaScript

+ +

Le code JavaScript suivant sera enregistré dans le fichier "fibonacci.js" auquel on fera référence dans le document HTML ci-après.

+ +
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 défini la propriété onmessage avec une fonction qui recevra les messages envoyés au worker via postMessage(). On initie alors la récursion et on déclenche des copies du worker afin de gérer chacune des itérations liées au calcul.

+ +

HTML

+ +
<!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 crée un élément div avec l'identifiant result. C'est cet élément qui sera utilisé afin d'afficher le résultat.

+ +

Ensuite, on lance le worker. Après avoir initié le worker, on configure le gestionnaire d'évènement onmessage afin d'afficher le résultat 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 démarrer.

+ +

Essayer cet exemple.

+ +

Répartir des tâches entre plusieurs workers

+ +

Les ordinateurs dotés de plusieurs coeurs se généralisent et il peut s'avérer utile de fragmenter une tâche complexe entre différents workers afin de tirer parti des différents coeurs du processeur.

+ +

Autres workers

+ +

En plus des web workers (dédiés et partagés), il existe d'autres types de workers :

+ + + +

Fonctions et interfaces disponibles pour les workers

+ +

La plupart des fonctionnalités JavaScript standard peuvent être utilisées dans les web workers, dont :

+ + + +

En 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 fonctionnalités disponibles pour les workers, voir  les fonctions et interfaces disponibles pour les workers.

+
+ +

Spécifications

+ + + + + + + + + + + + + + +
SpécificationÉtatCommentaires
{{SpecName('HTML WHATWG', '#workers', 'Web workers')}}{{Spec2('HTML WHATWG')}}
+ +

Compatibilité des navigateurs

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FonctionnalitéChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Support simple4[1]{{CompatGeckoDesktop("1.9.1")}}10.010.6[1]4[2][5]
Workers partagés4[1]{{CompatGeckoDesktop(29)}}{{CompatNo}}10.65
+ {{CompatNo}} 6.1[4]
Echange de données via le clonage structuré13{{CompatGeckoDesktop(8)}}10.011.56
Echange de données via les objets transférables17 {{property_prefix("webkit")}}
+ 21
{{CompatGeckoDesktop(18)}}{{CompatNo}}156
Variable globale {{domxref("window.URL", "URL")}}10[3]
+ 23
{{CompatGeckoDesktop(21)}}11156[3]
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FonctionnalitéAndroidChrome pour AndroidFirefox Mobile (Gecko)Firefox OS (Gecko)IE PhoneOpera MobileSafari Mobile
Support simple4.44[1]3.51.0.110.011.5[1]5.1[2][5]
Shared workers{{CompatNo}}4[1]81.0.1{{CompatNo}}{{CompatNo}}{{CompatNo}}
Echange de données via le clonage structuré{{CompatNo}}481.0.1{{CompatNo}}{{CompatNo}}{{CompatNo}}
Echange de données via les objets transférables{{CompatNo}}{{CompatNo}}181.0.1{{CompatNo}}{{CompatNo}}{{CompatNo}}
+
+ +

[1] Chrome et Opera provoquent une erreur "Uncaught SecurityError: Failed to construct 'Worker': Script at 'file:///Path/to/worker.js' cannot be accessed from origin 'null'." lorsqu'on souhaite accéder au worker en local. Il faut un domaine réel pour le faire.

+ +

[2] Avec Safari 7.1.2, on peut appeler console.log depuis un worker mais cela n'affichera rien dans la console. Les versions antérieures ne permettent pas d'appeler console.log depuis un worker.

+ +

[3] Cette fonctionnalité est préfixée : webkitURL.

+ +

[4] Safari a retiré la prise en charge de SharedWorker.

+ +

[5] Avec Safari v.12+ (iOS & MacOS), les web workers sont uniquement démarrés après une interaction utilisateur (un clic par exemple). Les workers pourront être créés mais ne seront pas exécutés tant qu'ils n'y aura pas eu d'interaction.

+ +

Voir aussi

+ + diff --git a/files/fr/web/api/web_workers_api/utilisation_des_web_workers/index.html b/files/fr/web/api/web_workers_api/utilisation_des_web_workers/index.html deleted file mode 100644 index b1330f34bf..0000000000 --- a/files/fr/web/api/web_workers_api/utilisation_des_web_workers/index.html +++ /dev/null @@ -1,633 +0,0 @@ ---- -title: Utilisation des web workers -slug: Web/API/Web_Workers_API/Utilisation_des_web_workers -tags: - - Avancé - - Guide - - JavaScript - - Web Workers -translation_of: Web/API/Web_Workers_API/Using_web_workers ---- -
{{DefaultAPISidebar("Web Workers API")}}
- -

Les Web Workers sont un outil permettant au contenu web d'exécuter des scripts dans des tâches (threads) d'arrière-plan. Le thread associé au worker peut réaliser des tâches sans qu'il y ait d'interférence avec l'interface utilisateur. De plus, les web workers peuvent réaliser des opérations d'entrée/sortie grâce à XMLHttpRequest (bien que les attributs responseXML et channel soient nécessairement vides dans ces cas). Une fois créé, un worker peut envoyer des messages au code JavaScript qui l'a créé. De même, le script initial peut envoyer des messages au worker. Cette communication s'effectue grâce à des gestionnaires d'évènements. Dans cet article, nous verrons une introduction à l'utilisation des web workers.

- -

L'API Web Workers

- -

Un worker est un objet créé à l'aide d'un constructeur (par exemple {{domxref("Worker.Worker", "Worker()")}}) et qui exécute un fichier JavaScript donné. Ce fichier contient le code qui sera exécuté par le thread du worker. Les workers sont exécutés dans un contexte global qui n'est pas celui du document (généralement {{domxref("window")}}). Aussi, si, dans un worker, on utilise {{domxref("window")}} pour accéder à la portée globale (plutôt que {{domxref("window.self","self")}}), cela provoquera une erreur.

- -

Le contexte du worker est représenté par un objet {{domxref("DedicatedWorkerGlobalScope")}} pour les workers dédiés et par un objet {{domxref("SharedWorkerGlobalScope")}} sinon. Un worker dédié est uniquement accessible au travers du script qui l'a déclenché tandis qu'un worker partagé peut être utilisé par différents scripts.

- -
-

Note : Voir la page d'entrée pour l'API Web Workers pour consulter la documentation de référence sur les workers et d'autres guides.

-
- -

Il est possible d'exécuter n'importe quel code JavaScript dans le thread du worker, à l'exception des méthodes de manipulation du DOM ou de certaines propriétés et méthodes rattachées à {{domxref("window")}}. On notera cependant qu'on peut tout à fait utiliser certaines API rendues disponibles via window comme les WebSockets, les API de stockage de données telles que IndexedDB. Pour plus de détails, voir les fonctions et classes disponibles au sein des workers.

- -

Les données sont échangées entre le thread du worker et le thread principal par l'intermédiaire 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 (le message sera contenu dans l'attribut data de l'évènement {{event("Message")}} associé). Les données sont copiées dans le message, elles ne sont pas partagées.

- -

Les workers peuvent également déclencher la création d'autres workers tant que ceux-ci restent hébergés sur la même origine que la page parente. De plus, les workers pourront utiliser XMLHttpRequest pour effectuer des opérations réseau mais les attributs responseXML et channel de XMLHttpRequest renverront nécessairement null.

- -

Les workers dédiés

- -

Comme indiqué plus haut, un worker dédié 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 dédié simple. Dans cet exemple, nous souhaitons multiplier deux nombres. Ces nombres sont envoyés à un worker dédié puis le résultat 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 détails plus avancés dans la suite de cet article.

- -

Détecter la possibilité d'utiliser les workers

- -

Afin de gérer une meilleure amélioration progressive, une rétro-compatibilité et de présenter des messages d'erreur adéquats, il pourra être utile d'envelopper le code relatif au worker de la façon suivante (main.js) :

- -
if (window.Worker) {
-  ...
-}
- -

Initier un worker dédié

- -

La création d'un nouveau worker est assez simple. On appellera le constructeur {{domxref("Worker.Worker", "Worker()")}} en indiquant l'URI du script à exécuter dans le thread associé au worker (main.js) :

- -
-
var monWorker = new Worker('worker.js');
-
-
- -

Envoyer des messages au worker et y réagir

- -

L'intérêt principal des workers repose sur l'échange de messages à l'aide de la méthode {{domxref("Worker.postMessage", "postMessage()")}} et grâce au gestionnaire d'évènement {{domxref("Worker.onmessage", "onmessage")}}. Lorsqu'on souhaite envoyer un message au worker, on enverra des messages de la façon 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 éléments {{htmlelement("input")}} représentés 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 utilisés pour échanger n'importe quel type de valeur.

- -

Dans le worker, on peut réagir au message reçu grâce à un gestionnaire d'évènement comme celui-ci (worker.js) :

- -
onmessage = function(e) {
-  console.log('Message reçu depuis le script principal.');
-  var workerResult = 'Résultat : ' + (e.data[0] * e.data[1]);
-  console.log('Envoi du message de retour au script principal');
-  postMessage(workerResult);
-}
- -

Le gestionnaire onmessage permet d'exécuter du code lorsqu'un message est reçu. Le message même est disponible grâce à l'attribut data de l'évènement. Dans cet exemple, nous multiplions simplement les deux nombres avant d'utiliser postMessage() à nouveau afin d'envoyer le résultat via un message destiné au thread principal.

- -

De retour dans le thread principal, nous pouvons utiliser onmessage à nouveau pour réagir à la réponse provenant du worker :

- -
monWorker.onmessage = function(e) {
-  resultat.textContent = e.data;
-  console.log('Message reçu depuis le worker');
-}
- -

Ici, nous récupérons les données grâce à l'attribut data de l'évènement et nous mettons à jour le contenu du paragraphe avec l'attribut textContent de l'élément. Ainsi, l'utilisateur peut visualiser le résultat du calcul.

- -
Note : On notera que onmessage et postMessage() doivent être rattachés à un objet Worker lorsqu'ils sont utilisés depuis le thread principal (ici, c'était monWorker) mais pas lorsqu'ils sont employés depuis le worker. En effet, dans le worker, c'est le worker qui constitue la portée globale et qui met à disposition ces méthodes.
- -
Note : Lorsqu'un message est envoyé d'un thread à l'autre, ses données sont copiées. Elles ne sont pas partagées. Voir ci-après pour plus d'explications à ce sujet.
- -

Clôturer un worker

- -

Si on doit arrêter un worker immédiatement, on pourra utiliser la méthode {{domxref("Worker", "terminate")}} depuis le thread principal :

- -
monWorker.terminate();
- -

Lorsque cette méthode exécuté, le thread associé au worker est tué immédiatement.

- -

Gérer les erreurs

- -

Lorsqu'une erreur d'exécution se produit avec le worker, son gestionnaire d'évènement onerror est appelé et reçoit un évènement error qui implémente l'interface ErrorEvent.

- -

Cet évènement ne bouillonne (bubble) pas et peut être annulé. Pour empêcher les conséquences par défaut, on pourra utiliser la méthode preventDefault() rattachée à l'évènement d'erreur.

- -

L'évènement décrivant l'erreur possède notamment trois propriétés intéressantes :

- -
-
message
-
Un message d'erreur compréhensible par un humain.
-
filename
-
Le nom du fichier pour lequel l'erreur s'est produite.
-
lineno
-
Le numéro de ligne au sein du fichier responsable de l'erreur.
-
- -

Initier des workers fils

- -

Les workers peuvent également engendrer d'autres workers. Ces workers-fils doivent être hébergés sur la même origine que la page initiale. De plus, les URI des workers-fils sont résolues relativement à l'emplacement du worker père (plutôt que par rapport à la page parente). Ces contraintes permettent de simplifier le suivi des dépendances.

- -

Importer des scripts et des bibliothèques

- -

Les threads d'exécution des workers peuvent accéder à la fonction globale importScripts(), qui leur permet d'importer des scripts. Cette fonction prend zéro à plusieurs URL en paramètres et importe les ressources associées. 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'exécutera. Chaque script pourra ainsi mettre à disposition des objets globaux qui pourront être utilisés par le worker. Si le script ne peut pas être chargé, une exception NETWORK_ERROR sera levée et le code assicé ne sera pas exécuté. Le code exécuté précédemment (y compris celui-ci reporté à l'aide de {{domxref("window.setTimeout()")}}) continuera cependant d'être fonctionnel. Les déclarations de fonction situées après importScripts() sont également exécutées car évaluées avant le reste du code.

- -
Note : Les scripts peuvent être téléchargés dans n'importe quel ordre mais ils seront exécutés dans l'ordre des arguments passés à importScripts() . Cet exécution est effectuée de façon synchrone et importScripts() ne rendra pas la main tant que l'ensemble des scripts n'auront pas été chargés et exécutés.
- -

Les workers partagés

- -

Un worker partagé est accessible par plusieurs scripts (même si ceux-ci proviennent de différentes fenêtres, 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 dédié. Il diffère car il possède deux fonctions, gérées 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 même worker pour réaliser le calcul demandé.

- -

Ici, nous nous intéresserons particulièrement aux différences entre les workers dédiés et les workers partagés. Dans cet exemple, nous aurons deux pages HTML, chacune utilisant du code JavaScript employant le même worker.

- -
-

Note : Si on peut accéder à un worker partagé depuis différents contextes de navigations, ces contextes de navigation doivent néanmoins partager la même origine (même protocole, même hôte, même port).

-
- -
-

Note : Dans Firefox, les workers partagés ne peuvent pas être partagés entre les documents chargés en navigation privée et les documents chargés en navigation classique ({{bug(1177621)}}).

-
- -

Initier un worker partagé

- -

La création d'un nouveau worker partagé est assez semblable à la création d'un worker dédié. On utilise alors un constructeur différent :

- -
var monWorker = new SharedWorker('worker.js');
- -

Une différence fondamentale avec les workers dédiés 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 dédiés, ce port est ouvert implicitement).

- -

La connexion au port doit être démarrée implicitement avec l'utilisation du gestionnaire d'évènement onmessage ou explicitement avec la méthode start() avant qu'un message soit envoyé. On utilisera uniquement start() si l'évènement message est détecté avec la méthode addEventListener().

- -
-

Note : Lorsqu'on utilise la méthode start() afin d'ouvrir le port de connexion, celle-ci doit être appelée de part et d'autre (depuis le thread parent et depuis le worker) si on souhaite disposer d'une connexion bidirectionnelle.

-
- -

Échanger des messages avec un worker partagé et y réagir

- -

On peut alors envoyer des messages au worker. Dans le cas d'un worker partagé, la méthode postMessage() doit être appelée 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 côté du worker, les choses sont également légèrement plus compliquées (voir worker.js) :

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

On commence par utiliser le gestionnaire onconnect afin de déclencher du code à la connexion au port (c'est-à-dire lorsque le gestionnaire onmessage est déclaré depuis le thread parent ou lorsque la méthode start() est invoquée explicitement depuis le thread parent).

- -

On utilise l'attribut ports de l'évènement afin de récupérer le port utilisé et on le place dans une variable.

- -

Ensuite, sur ce port, on ajoute un gestionnaire d'évènement pour l'évènement message afin de faire les calculs et de renvoyer le résultat au thread principal. En définissant 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 nécessaire d'invoquer port.start().

- -

Enfin, dans le script de la page, on gère le message du résultat (voir multiply.js et square.js):

- -
monWorker.port.onmessage = function(e) {
-  result2.textContent = e.data;
-  console.log('Message reçu depuis le worker');
-}
- -

Lorsqu'un message provient du port associé au worker, on vérifie son type puis on insère le résultat dans le paragraphe associé.

- -

Sûreté des threads

- -

L'interface {{domxref("Worker")}} engendre des threads au sens du système d'exploitation. Certains développeurs avertis pourront se demander si cette communication à base de threads ne peut pas générer d'effets indésirables tels que des situations de compétition (race conditions).

- -

Toutefois, la communication entre les web workers est contrôlée explicitement dans les scripts et il n'y a pas d'accès aux composants ou au DOM qui ne seraient pas sûrs à ce niveau. De plus, la communication entre les threads s'effectue par recopie de données. Aussi, s'il n'est théoriquement pas impossible de ne pas avoir de tels problèmes, il faudrait les chercher pour les provoquer.

- -

Règles de sécurité du contenu (content security policy, CSP)

- -

Les workers disposent de leur propre contexte d'exécution, distinct de celui du document qui les a créés. Aussi, en général, les workers ne sont pas gérés par la politique de sécurité de contenu du document (ou du worker parent) responsable de leur création. Ainsi, si un document est servi avec l'en-tête suivant :

- -
Content-Security-Policy: script-src 'self'
- -
Cette règle empêchera n'importe quel script inclus dans le document d'utiliser eval(). Toutefois, si le script génère un worker, le code exécuté par ce worker pourra utiliser eval().
-
-Pour appliquer une règle de sécurité au worker, il faudra fournir un en-tête Content-Security-Policy approprié pour la requête responsable du service du script du worker.
-
-Si l'origine du script du worker est un identifiant global unique (si son URL utilise le schéma data:// ou blob:// par exemple), le worker héritera du CSP associé au document responsable de sa création.
- -
- -

Échanger des données avec les workers : plus de détails

- -

Les données échangées entre le document principal et les workers sont copiées et non partagées. Lorsqu'ils sont envoyés au worker, les objets sont sérialisés (puis désérialisés à leur réception). La page et le worker ne partagent pas le même exemplaire et on a donc deux versions d'une part et d'autre. La plupart des navigateurs implémentent cette approche avec une clonage structurel.

- -

Pour illustrer ce point, on prendra une fonction intitulée emulateMessage() et qui simule le comportement d'une valeur clonée (pas partagée) avec un worker attaché à la page principale et réciproquement :

- -
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 clonée et non partagée est appelée un message. Les messages peuvent être envoyés et reçus grâce à postMessage() et au gestionnaire d'évènement pour message (dont l'attribut {{domxref("MessageEvent.data", "data")}} contiendra les données copiées).

- -

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 sérialiser aussi bien des données JSON que d'autres formats et permet notamment de gérer les références circulaires (ce que JSON ne permet pas de gérer nativement).

- -

Les objets transférables - échanger des données avec transfert de la propriété

- -

Chrome 17+ et Firefox 18+ permettent également d'échanger certains types d'objet (qualifiés de transférables et qui implémentent l'interface {{domxref("Transferable")}}) avec des workers et à haute performance. Les objets transférables sont passés d'un contexte à l'autre avec une opération zero-copy qui permet d'obtenir des améliorations sensibles lors de l'échange de données volumineuses. On peut voir cela comme un passage de référence du monde C/C++ mais les données qui sont transférées depuis le contexte appelant ne sont plus disponibles dans ce contexte après le transfert. La propriété des données est transférée au nouveau contexte. Ainsi, lorsqu'on transfère un objet {{domxref("ArrayBuffer")}} depuis l'application principale vers le worker, l'objet {{domxref("ArrayBuffer")}} de départ est nettoyé et ne peut plus être utilisé, son contenu est (littéralement) transféré au contexte du worker.

- -
// Créer 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 transférables, aux performances associées et à la détection de ces fonctionnalités, on pourra lire Transferable Objects: Lightning Fast.

-
- -

Workers embarqués

- -

Il n'existe pas de méthode standard pour embarquer le code d'un worker dans une page web à la façon des éléments {{HTMLElement("script")}}. Toutefois, un élément {{HTMLElement("script")}}, qui ne possède pas d'attribut src, qui possède un attribut type ne correspondant pas à un type MIME exécutable pourra être considéré comme un bloc de données pouvant être utilisé par JavaScript. Ces blocs de données sont une fonctionnalité HTML5 qui permet de transporter n'importe quelle donnée textuelle. On pourrait donc embarquer un worker de cette façon :

- -
<!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 crée une nouvelle propriété 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 démarre le worker.
-  window.onload = function() { document.worker.postMessage(''); };
-</script>
-</head>
-<body><div id="logDisplay"></div></body>
-</html>
- -

Le worker embarqué est désormais injecté dans la propriété document.worker.

- -

On notera également qu'on peut convertir une fonction en un Blob et générer 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)
-}
- -

Autres exemples

- -

Dans cette section nous voyons d'autres exemples d'application.

- -

Effectuer des calculs en arrière-plan

- -

Les workers sont notamment utiles pour réaliser des opérations 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.

- -

JavaScript

- -

Le code JavaScript suivant sera enregistré dans le fichier "fibonacci.js" auquel on fera référence dans le document HTML ci-après.

- -
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 défini la propriété onmessage avec une fonction qui recevra les messages envoyés au worker via postMessage(). On initie alors la récursion et on déclenche des copies du worker afin de gérer chacune des itérations liées au calcul.

- -

HTML

- -
<!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 crée un élément div avec l'identifiant result. C'est cet élément qui sera utilisé afin d'afficher le résultat.

- -

Ensuite, on lance le worker. Après avoir initié le worker, on configure le gestionnaire d'évènement onmessage afin d'afficher le résultat 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 démarrer.

- -

Essayer cet exemple.

- -

Répartir des tâches entre plusieurs workers

- -

Les ordinateurs dotés de plusieurs coeurs se généralisent et il peut s'avérer utile de fragmenter une tâche complexe entre différents workers afin de tirer parti des différents coeurs du processeur.

- -

Autres workers

- -

En plus des web workers (dédiés et partagés), il existe d'autres types de workers :

- - - -

Fonctions et interfaces disponibles pour les workers

- -

La plupart des fonctionnalités JavaScript standard peuvent être utilisées dans les web workers, dont :

- - - -

En 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 fonctionnalités disponibles pour les workers, voir  les fonctions et interfaces disponibles pour les workers.

-
- -

Spécifications

- - - - - - - - - - - - - - -
SpécificationÉtatCommentaires
{{SpecName('HTML WHATWG', '#workers', 'Web workers')}}{{Spec2('HTML WHATWG')}}
- -

Compatibilité des navigateurs

- -
{{CompatibilityTable}}
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Support simple4[1]{{CompatGeckoDesktop("1.9.1")}}10.010.6[1]4[2][5]
Workers partagés4[1]{{CompatGeckoDesktop(29)}}{{CompatNo}}10.65
- {{CompatNo}} 6.1[4]
Echange de données via le clonage structuré13{{CompatGeckoDesktop(8)}}10.011.56
Echange de données via les objets transférables17 {{property_prefix("webkit")}}
- 21
{{CompatGeckoDesktop(18)}}{{CompatNo}}156
Variable globale {{domxref("window.URL", "URL")}}10[3]
- 23
{{CompatGeckoDesktop(21)}}11156[3]
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéAndroidChrome pour AndroidFirefox Mobile (Gecko)Firefox OS (Gecko)IE PhoneOpera MobileSafari Mobile
Support simple4.44[1]3.51.0.110.011.5[1]5.1[2][5]
Shared workers{{CompatNo}}4[1]81.0.1{{CompatNo}}{{CompatNo}}{{CompatNo}}
Echange de données via le clonage structuré{{CompatNo}}481.0.1{{CompatNo}}{{CompatNo}}{{CompatNo}}
Echange de données via les objets transférables{{CompatNo}}{{CompatNo}}181.0.1{{CompatNo}}{{CompatNo}}{{CompatNo}}
-
- -

[1] Chrome et Opera provoquent une erreur "Uncaught SecurityError: Failed to construct 'Worker': Script at 'file:///Path/to/worker.js' cannot be accessed from origin 'null'." lorsqu'on souhaite accéder au worker en local. Il faut un domaine réel pour le faire.

- -

[2] Avec Safari 7.1.2, on peut appeler console.log depuis un worker mais cela n'affichera rien dans la console. Les versions antérieures ne permettent pas d'appeler console.log depuis un worker.

- -

[3] Cette fonctionnalité est préfixée : webkitURL.

- -

[4] Safari a retiré la prise en charge de SharedWorker.

- -

[5] Avec Safari v.12+ (iOS & MacOS), les web workers sont uniquement démarrés après une interaction utilisateur (un clic par exemple). Les workers pourront être créés mais ne seront pas exécutés tant qu'ils n'y aura pas eu d'interaction.

- -

Voir aussi

- - -- cgit v1.2.3-54-g00ecf