Les web workers sont des threads JavaScript qui permettent de déléguer des tâches longues et/ou intensives en CPU à des processus en arrière-plan, afin d'éviter de bloquer l'interface de l'application web.
Cela signifie que vous pouvez exécuter du code JavaScript qui prend du temps à s'exécuter, comme des calculs complexes, des opérations de traitement de données ou des téléchargements de fichiers, sans perturber la réactivité de votre application et notamment déclencher l'apparition d'un avertissement du navigateur de type Attention : le script ne répond pas, voulez-vous attendre ou l'arrêter ?
.
Le support de cette technique est très bon de nos jours avec tous les navigateurs majeurs desktop et mobiles, depuis IE10 inclus et Android 4.4, soit depuis environ 2014. Reportez-vous au tableau Caniuse : Web Workers pour tous les détails.
Pour utiliser un web worker, vous devez créer un fichier JavaScript séparé qui contiendra le code à exécuter en arrière-plan, puis utiliser l'API des web workers pour instancier un web worker à partir de ce fichier et lui envoyer des messages pour lancer des tâches ou obtenir des résultats. Le web worker exécutera le code de manière asynchrone, de sorte que vous pouvez continuer à interagir avec l'application pendant que le code du web worker s'exécute en arrière-plan.
Cas d'usage
On utilise des workers dans les cas suivants (et d'autres) pour éviter de bloquer l'interface :
- Calculs complexes : Si vous avez besoin de calculer des valeurs complexe qui prennent du temps à s'exécuter, vous pouvez utiliser un web worker pour déléguer ces calculs à un processus en arrière-plan et éviter de bloquer l'interface utilisateur (UI) de l'application.
- Traitement de données : Si vous avez besoin de traiter de grandes quantités de données qui prennent du temps à s'exécuter, vous pouvez utiliser un web worker pour déléguer ce traitement à un processus en arrière-plan et éviter de surcharger le processeur.
- Téléchargement de fichiers : Si vous avez besoin de télécharger de gros fichiers qui prennent du temps, vous pouvez utiliser un web worker pour déléguer cette tâche.
- Animations complexes : Si vous avez besoin d'animations complexes qui prennent du temps à s'exécuter, vous pouvez utiliser un web worker pour déléguer l'exécution de ces animations à un processus en arrière-plan et économiser les ressources.
Une démo vaut mille mots
⚗️ L'exemple le plus parlant peut être retrouvé en ligne : Sorting 50K Array with Bubble Sort.
Il s'agit d'effectuer un tri sur un tableau par algorithme bubble sort (tri à bulles) avec 2 méthodes : - sans worker : de manière classique, cet exercice bloque l'interface, on peut le voir aux styles figés du bouton, à l'absence de l'animation de la barre de progression, et de réactivité globale. - avec worker : le document principal continue de vivre avec ses états, animations, interactions, sans bloquer durant la réalisation du calcul.
Limitations
Par leur nature, les workers ont des limites.
- Ils ne peuvent pas accéder à certaines propriétés de l'objet
window
, commewindow.document
, car ils sont exécutés dans un contexte de thread séparé et ne partagent pas le même espace de nomage que le code principal de l'application. Cela également pour éviter des conflits d'accès aux ressources avec les scripts de la page principale. - Ils ne peuvent pas afficher de fenêtres de dialogue ou des boîtes de message, car ils n'ont pas accès aux fonctionnalités de l'interface graphique de l'application.
- Ils ne peuvent pas utiliser certains objets de l'API navigation, comme
history
oulocation
, car ils n'ont pas accès à la barre d'adresse ou à l'historique de navigation de l'application. - Ils ne peuvent pas interagir directement avec le DOM de la page, car ils n'ont pas accès aux éléments HTML de l'application. Pour mettre à jour le DOM depuis un web worker, vous devez en réalité envoyer un message au processus principal de l'application, qui lui même interviendra.
- Ils ne peuvent pas accéder à certaines APIs du navigateur, comme celles qui gèrent les cookies ou les notifications, car elles sont liées à l'interface de l'application, du document, du navigateur.
Initialisation d'un worker
Créez un fichier JavaScript séparé qui contiendra le code à exécuter en arrière-plan. Ce fichier sera appelé "fichier de travail".
Dans votre code principal, utilisez le constructeur Worker()
de l'objet Worker pour instancier un web worker à partir du fichier de travail. Cette méthode prend en paramètre le chemin vers le fichier :
const worker = new Worker('path/to/worker.js');
Vous pouvez maintenant envoyer des messages au web worker en utilisant la méthode postMessage()
de l'objet Worker, et par la même occasion lancer les opérations :
worker.postMessage({ message: 'Hello, worker!' });
Vous pouvez également définir un gestionnaire d'événements qui sera appelé chaque fois que le web worker envoie un message au code principal en utilisant la méthode addEventListener()
de l'objet Worker :
worker.addEventListener('message', (event) => {
console.log(event.data);
});
Lorsque vous avez fini de travailler avec le web worker, vous pouvez le fermer en utilisant la méthode terminate()
:
worker.terminate();
Vous pouvez également utiliser l'API des web workers pour gérer les erreurs et les exceptions qui se produisent dans le code du web worker en définissant des gestionnaires d'événements pour les événements error
et messageerror
.
Rappelez-vous que le code du fichier de travail est exécuté dans un contexte de thread séparé, de sorte que toutes les variables et fonctions définies dans le fichier de travail ne seront pas disponibles dans le code principal de l'application. Pour communiquer entre le code principal et le web worker, vous devez utiliser l'API des web workers pour envoyer et recevoir des messages.
Exemple concret
Voici un exemple basique mais concret d'utilisation d'un web worker pour effectuer des calculs. Attention les instructions sont présentées de manière minimaliste, il serait plus propre de les réunir dans un objet, une classe, etc. pour ne pas manipuler des variables globales.
On crée un fichier JavaScript séparé qui contiendra le code à exécuter en arrière-plan. Par exemple, le fichier de travail pourrait ressembler à ceci :
// La fonction de calcul complexe
function calculate(data) {
// Effectuez les calculs complexes ici
const result = /* résultat des calculs */;
postMessage(result);
}
// Attendez un message du code principal
onmessage = function(e) {
// Appelez la fonction de calcul avec les données reçues
calculate(e.data);
};
Dans votre code principal, utilisez la méthode Worker()
pour instancier un web worker à partir du fichier de travail :
const worker = new Worker('worker.js');
Définissez un gestionnaire d'événements qui sera appelé chaque fois que le web worker envoie un message au code principal en utilisant la méthode addEventListener() de l'objet Worker :
worker.addEventListener('message', (event) => {
console.log(event.data); // Affiche le résultat des calculs
});
Envoyez un message au web worker en utilisant la méthode postMessage() de l'objet Worker et en lui envoyant les données à traiter :
worker.postMessage({ data: /* données à traiter */ });
Le web worker recevra le message et exécutera la fonction de calcul en arrière-plan, en utilisant les données envoyées par le code principal. Lorsque les calculs sont terminés, le web worker enverra un message au code principal avec le résultat, qui sera affiché dans le gestionnaire d'événements défini.
Commenter
Vous devez être inscrit et identifié pour utiliser cette fonction.
Connectez-vous (déjà inscrit)
Pas encore inscrit ? C'est très simple et gratuit.