angular-web-workers
AngularJS

AngularJS : intégrer des Web Workers

Dans ce court billet, nous allons voir comment tirer profit de l’API Web Workers proposée par HTML5, au sein d’une application AngularJS. Cette API va nous permettre de créer des Threads en tâche de fond, dont le rôle sera de réaliser des taches gourmandes en mémoire, évidement de façon asynchrone et sans détériorer l’expérience utilisateur (en ralentissant l’interface graphique par exemple).

Tout d’abord, avant de pouvoir intégrer notre Web Worker dans notre application AngularJS, il va falloir créer un script JavaScript contenant l’implémentation du Web Worker. Voici un exemple d’implémentation de ce script (que nous plaçons dans un répertoire ./app/workers/) ./app/workers/MyWorker.js :

function process(myData) {
    // vos opérations gourmandes ici...
}
self.addEventListener('message', function(e) {
    self.postMessage(process(e.data.myData));
}, false);

Rien de bien méchant, c’est un Web Worker classique

Maintenant, pour bien respecter la philosophie d’AngularJS, le meilleur endroit pour intégrer ce Web Worker est bien évidement dans un service, vu la nature asynchrone des Web Workers.

Créons donc un service AngularJS :

'use strict';

angular.module('frontApp')
  .factory('WorkerService', function ($q) {

    var worker = new Worker('workers/MyWorker.js');
    var defer;
    worker.addEventListener('message', function(e) {
      // traitements additionnels ...
      defer.resolve(e.data);
    }, false);

    return {
        processData : function(data){
            defer = $q.defer();
            worker.postMessage({
              'myData': data
            });
            return defer.promise;
        }
    };
  });

Explications…

Le service WorkerService expose une méthode processData(data) qui va nous permettre d’exécuter notre code (qui sera délégué au Web Worker). Cette méthode déclare et retourne une Promise grâce au service $q d’AngularJS (voir les avantages des Promises en JavaScript). Cette promise sera résolue lorsque le Web Worker retournera sa réponse, à la fin du traitement.

Notez également que si vous devez réaliser des traitements additionnels sur le résultat retourné par le Web Worker, vous pouvez (devez) le faire lors de la réception de l’event message (au sein du WorkerService):

worker.addEventListener('message', function(e) {
   // traitements additionnels sur e.data ...
   defer.resolve(e.data);
}, false);

L’idée ici est de ne pas (voire JAMAIS)  réaliser des traitements modifiants l’état de votre application AngularJS dans les contrôlleurs. Privilégiez les Services, Factories, Directives et Filters (dans une certaines mesure pour les deux derniers).

Et pour finir, nous allons appeler notre service WorkerService comme d’habitude depuis un contrôleur comme ceci :

'use strict';

angular.module('MyApp')
    .controller('MainCtrl', function($scope, MyWorker) {
    // ...
    MyWorker.processData(inputData).then(function(outputData){
        $scope.myData = outputData; 
    });
    // ... 
});

Et voilà ! Vous venez d’intégrer votre premier Web Worker dans votre application AngularJS.

Petit bémol tout de même : L’API Web Worker n’étant pas encore très supportée par tous les navigateurs (voir le niveau du support), je vous recommande donc de l’utiliser en toute connaissance de cause.

Standard