web-dev-qa-db-fra.com

AngularJS et travailleurs Web

Comment angularJS peut-il utiliser des travailleurs Web pour exécuter des processus en arrière-plan? Y a-t-il une tendance que je devrais suivre pour faire cela?

Actuellement, j'utilise un service qui a le modèle dans un outil Web distinct. Ce service implémente des méthodes telles que:

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 d’abstraire le fait qu’elle est exécutée dans un thread séparé et que je pourrais également fournir une implémentation qui interroge un serveur ou même un qui effectue cette action dans le même thread.

Depuis que javascript est nouveau pour moi 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 peut-être Angular qui est ce que j’utilise, offre une sorte Cela introduit également un changement dans mon architecture puisque le travailleur doit explicitement Transférer les changements au contrôleur, qui met ensuite à jour ses valeurs et ensuite cela se reflète dans la vue, suis-je en train d’ingénierie? C'est un peu frustrant que les travailleurs Web me "protègent" tellement contre les erreurs en ne me permettant pas de partager la mémoire, etc.

71
arg20

La communication avec les travailleurs Web s'effectue via un mécanisme de messagerie. L'interception de ces messages a lieu lors d'un rappel. Dans AngularJS, le meilleur emplacement pour placer un travailleur Web est dans un service, comme vous l'avez noté. La meilleure façon de gérer cela consiste à utiliser des promesses, avec lesquelles Angular fonctionne à merveille avec.

Voici un exemple de webworker dans un 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, quelle que soit l'entité externe qui accède au service Hello World n'a pas besoin de se soucier des détails d'implémentation de HelloWorldService - HelloWorldService pourrait probablement traiter les données sur un web worker, sur http ou effectuez le traitement sur place.

J'espère que cela a du sens.

96
ganaraj

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 de travail dans un fichier séparé rend l’intention d’un service difficile à lire et introduit des dépendances pour les URL de fichiers statiques dans votre code d’application angular. Ce problème peut être atténué à l’aide de la URL.createObjectUrl () qui peut être utilisé pour créer une URL pour une chaîne JavaScript, ce qui nous permet de spécifier le code de l'ouvrier dans le même fichier que celui qui crée l'ouvrier.

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 le travailleur et les contextes du fil principal complètement séparés pour éviter les situations dans lesquelles des impasses, des livelocks, etc. Mais cela signifie également que vous n'aurez pas accès à vos services angular) sans quelques manipulations. Le travailleur manque de certaines des choses auxquelles nous (et de manière angulaire) nous attendons lors de l'exécution de JavaScript dans le navigateur, comme la variable globale "document", etc. En "se moquant" de ces fonctions de navigateur obligatoires dans le travailleur, on peut obtenir angular à exécuter.

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, comme 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 souhaitons chez un travailleur. Ce que nous gagnons, c’est que nous pouvons exécuter des services angular angulaires chez un travailleur. Nous pouvons donc tester les services utilisés par le travailleur de la même manière que nous le ferions avec tout autre angular.

J'ai publié un article qui développe un peu plus ce sujet ici et créé un dépôt github qui crée un service mettant en œuvre les idées abordées ci-dessus ici

16
liket

J'ai trouvé un exemple pleinement fonctionnel de travailleurs Web dans Angular here

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 retourne le résultat.

11
Octane

Travailleur Web angulaire avec exemple d'interrogation

Lorsque vous traitez avec des ouvriers dans AngularJS, il est souvent nécessaire que votre script soit en ligne (si vous utilisez des outils de construction comme gulp/grunt) et nous pouvons y parvenir en utilisant l’approche suivante.

L'exemple ci-dessous montre également comment une interrogation peut être effectuée sur un serveur à l'aide de travailleurs:

Commençons par créer notre usine de travail:

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

Suivant de notre contrôleur appeler l'usine 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 depuis votre contrôleur!

Ceci est testé dans IE11 + et FF et Chrome

7
Chanakya Vadla

vous pouvez également consulter le angular 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 doc d'API: http://www.eslinstructor.net/ng-vkthread/demo/

--Vadim

2
vadimk