72 votes

AngularJS et les travailleurs web

Comment angularJS peut-il utiliser les web workers pour exécuter des processus en arrière-plan ? Y a-t-il un modèle que je devrais suivre pour faire cela ?

Actuellement, j'utilise un service dont le modèle se trouve dans un travailleur Web distinct. Ce service implémente des méthodes comme :

ClientsFacade.calculateDebt(client1); //Just an example..

Dans l'implémentation, cette méthode envoie un message au travailleur avec les données. Cela me permet de faire abstraction du fait qu'elle est exécutée dans un thread séparé et je pourrais également fournir une implémentation qui interroge un serveur ou même une implémentation qui effectue cette action dans le même thread.

Comme je suis novice en javascript et que je ne fais que recycler les connaissances que j'ai acquises sur d'autres plateformes, je me demande si c'est quelque chose que vous feriez ou si Angular, que j'utilise, offre une façon de le faire. De plus, cela introduit un changement dans mon architecture puisque le travailleur doit explicitement pousser les changements au contrôleur, qui met ensuite à jour ses valeurs et qui est ensuite reflété dans la vue, est-ce que je fais trop d'ingénierie ? C'est un peu frustrant que les travailleurs web me "protègent" autant des erreurs en ne me permettant pas de partager la mémoire, etc.

97voto

ganaraj Points 14228

La Communication avec les Web workers arrive à travers un mécanisme de messagerie. L'interception de ces messages qui se passe dans un appel de retour. Dans AngularJS, le meilleur emplacement pour mettre un site web du travailleur dans un service comme vous, dûment noté. La meilleure façon de traiter cette question est d'utiliser des promesses, qui Angulaire fonctionne étonnamment avec.

Voici un exemple d'un webworker en service

var app = angular.module("myApp",[]);

app.factory("HelloWorldService",['$q',function($q){

    var worker = new Worker('doWork.js');
    var defer = $q.defer();
    worker.addEventListener('message', function(e) {
      console.log('Worker said: ', e.data);
      defer.resolve(e.data);
    }, false);

    return {
        doWork : function(myData){
            defer = $q.defer();
            worker.postMessage(myData); // Send data to our worker. 
            return defer.promise;
        }
    };

});

Maintenant, quel que soit externe de l'entité qui accède Bonjour tout le Monde, le service n'a pas besoin de soins sur les détails de mise en œuvre de l' HelloWorldService - HelloWorldService pourrait probablement traiter les données sur un web worker, plus de http ou le traitement.

Espérons que cela a du sens.

17voto

liket Points 193

Une question très intéressante ! Je trouve la spécification du travailleur web un peu gênante (probablement pour de bonnes raisons, mais quand même gênante). La nécessité de conserver le code du travailleur dans un fichier séparé rend l'intention d'un service difficile à lire et introduit des dépendances aux URL de fichiers statiques dans le code de votre application angulaire. Ce problème peut être atténué en utilisant la fonction URL.createObjectUrl() qui peut être utilisée pour créer une URL pour une chaîne JavaScript. Cela nous permet de spécifier le code du travailleur dans le même fichier qui crée le travailleur.

var blobURL = URL.createObjectURL(new Blob([
    "var i = 0;//web worker body"
], { type: 'application/javascript' }));
var worker = new Worker(blobURL);

La spécification du travailleur web maintient également les contextes du travailleur et du thread principal complètement séparés afin d'éviter les situations où des blocages, des blocages en direct, etc. peuvent se produire. Mais cela signifie aussi que vous n'aurez pas accès à vos services angulaires dans le worker sans faire quelques manipulations. Le worker manque de certaines choses que nous (et angular) attendons lors de l'exécution de JavaScript dans le navigateur, comme la variable globale "document", etc. En "mockant" ces fonctionnalités requises du navigateur dans le worker, nous pouvons faire fonctionner angular.

var window = self;
self.history = {};
var document = {
    readyState: 'complete',
    cookie: '',
    querySelector: function () {},
    createElement: function () {
        return {
            pathname: '',
            setAttribute: function () {}
        };
    }
};

Certaines fonctionnalités ne fonctionneront évidemment pas, les liaisons avec le DOM, etc. Mais le framework d'injection et par exemple le service $http fonctionneront parfaitement, ce qui est probablement ce que nous voulons dans un worker. Ce que nous gagnons ainsi, c'est que nous pouvons exécuter des services angulaires standard dans un worker. Nous pouvons donc tester à l'unité les services utilisés dans le worker comme nous le ferions avec n'importe quelle autre dépendance angulaire.

J'ai fait un post qui développe un peu plus sur ce sujet aquí et créé un repo github qui crée un service qui met en œuvre les idées discutées ci-dessus aquí

11voto

Octane Points 104

J'ai trouvé un exemple entièrement fonctionnel de travailleurs web dans Angular. aquí

webworker.controller('webWorkerCtrl', ['$scope', '$q', function($scope, $q) {

    $scope.workerReplyUI;
    $scope.callWebWorker = function() {
        var worker = new Worker('worker.js');
        var defer = $q.defer();
        worker.onmessage = function(e) {
            defer.resolve(e.data);
            worker.terminate();
        };

        worker.postMessage("http://jsonplaceholder.typicode.com/users");
        return defer.promise;
    }

    $scope.callWebWorker().then(function(workerReply) {
        $scope.workerReplyUI = workerReply;
    });

}]);

Il utilise des promesses pour attendre que le travailleur renvoie le résultat.

8voto

chanu sukarno Points 723

Angular Web Worker avec exemple d'interrogation

Lorsque vous traitez avec les travailleurs dans AngularJS, il est souvent nécessaire que votre travailleur script à être en ligne (incase vous utilisez certains outils de construction comme gulp / grunt) et nous pouvons réaliser cela en utilisant l'approche suivante.

L'exemple ci-dessous montre également comment l'interrogation peut être faite au serveur en utilisant des travailleurs :

Tout d'abord, créons notre usine de travailleurs :

    module.factory("myWorker", function($q) {
    var worker = undefined;
    return {
        startWork: function(postData) {
            var defer = $q.defer();
            if (worker) {
                worker.terminate();
            }

            // function to be your worker
            function workerFunction() {
                var self = this;
                self.onmessage = function(event) {
                    var timeoutPromise = undefined;
                    var dataUrl = event.data.dataUrl;
                    var pollingInterval = event.data.pollingInterval;
                    if (dataUrl) {
                        if (timeoutPromise) {
                            setTimeout.cancel(timeoutPromise); // cancelling previous promises
                        }

                        console.log('Notifications - Data URL: ' + dataUrl);
                        //get Notification count
                        var delay = 5000; // poller 5sec delay
                        (function pollerFunc() {
                            timeoutPromise = setTimeout(function() {
                                var xmlhttp = new XMLHttpRequest();
                                xmlhttp.onreadystatechange = function() {
                                    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                                        var response = JSON.parse(xmlhttp.responseText);
                                        self.postMessage(response.id);
                                        pollerFunc();
                                    }
                                };
                                xmlhttp.open('GET', dataUrl, true);
                                xmlhttp.send();
                            }, delay);
                        })();
                    }
                }
            }
            // end worker function

            var dataObj = '(' + workerFunction + ')();'; // here is the trick to convert the above fucntion to string
            var blob = new Blob([dataObj.replace('"use strict";', '')]); // firefox adds user strict to any function which was blocking might block worker execution so knock it off

            var blobURL = (window.URL ? URL : webkitURL).createObjectURL(blob, {
                type: 'application/javascript; charset=utf-8'
            });

            worker = new Worker(blobURL);
            worker.onmessage = function(e) {
                console.log('Worker said: ', e.data);
                defer.notify(e.data);
            };
            worker.postMessage(postData); // Send data to our worker.
            return defer.promise;
        },
        stopWork: function() {
            if (worker) {
                worker.terminate();
            }
        }
    }
});

Ensuite, depuis notre contrôleur, nous appelons la fabrique de travailleurs :

var inputToWorker = {
    dataUrl: "http://jsonplaceholder.typicode.com/posts/1", // url to poll
    pollingInterval: 5 // interval
};

myWorker.startWork(inputToWorker).then(function(response) {
    // complete
}, function(error) {
    // error
}, function(response) {
    // notify (here you receive intermittent responses from worker)
    console.log("Notification worker RESPONSE: " + response);
});

Vous pouvez appeler myWorker.stopWork(); à tout moment pour mettre fin au travailleur de votre contrôleur !

Ceci a été testé dans IE11+, FF et Chrome.

2voto

vadimk Points 633

Vous pouvez aussi jeter un coup d'oeil au plugin angulaire https://github.com/vkiryukhin/ng-vkthread

qui vous permet d'exécuter une fonction dans un thread séparé. Utilisation de base :

/* function to execute in a thread */
function foo(n, m){ 
    return n + m;
}

/* create an object, which you pass to vkThread as an argument*/
var param = {
      fn: foo      // <-- function to execute
      args: [1, 2] // <-- arguments for this function
    };

/* run thread */
vkThread.exec(param).then(
   function (data) {
       console.log(data);  // <-- thread returns 3 
    }
);

Exemples et documentation API : http://www.eslinstructor.net/ng-vkthread/demo/

--Vadim

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X