it-swarm.com.de

AngularJS und Web Worker

Wie kann angleJS Web-Worker verwenden, um Prozesse im Hintergrund auszuführen? Gibt es ein Muster, dem ich folgen sollte?

Derzeit verwende ich einen Dienst, der das Modell in einem separaten Web-Worker hat. Dieser Dienst implementiert Methoden wie:

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

In der Implementierung sendet diese Methode eine Nachricht mit den Daten an den Worker. Auf diese Weise kann ich die Tatsache abstrahieren, dass die Ausführung in einem separaten Thread erfolgt, und ich kann auch eine Implementierung bereitstellen, die Abfragen für einen Server vornimmt oder sogar für einen, der diese Aktion im selben Thread ausführt.

Da Javascript für mich neu ist und ich nur das Wissen anderer Plattformen wiederverwende, frage ich mich, ob Sie dies tun würden oder vielleicht Angular das, was ich verwende, eine Art anbietet Dies bringt auch eine Änderung in meiner Architektur mit sich, da der Worker Änderungen explizit an den Controller senden muss, der dann seine Werte aktualisiert und dies in der Ansicht widerspiegelt Web-Worker "schützen" mich so sehr vor Unfug, indem sie mir nicht erlauben, Speicher etc. zu teilen.

71
arg20

Die Kommunikation mit Web-Mitarbeitern erfolgt über einen Messaging-Mechanismus. Das Abfangen dieser Nachrichten erfolgt in einem Rückruf. In AngularJS ist der beste Standort für einen Web-Worker ein Dienst, wie Sie ordnungsgemäß angegeben haben. Der beste Weg, um damit umzugehen, besteht darin, Versprechen zu verwenden, mit denen Angular erstaunlicherweise funktioniert.

Hier ist ein Beispiel eines webworker in einem 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;
        }
    };

});

Nun muss sich jede externe Entität, die auf den Hello World-Dienst zugreift, nicht um die Implementierungsdetails von HelloWorldService - HelloWorldService kümmern, um die Daten wahrscheinlich über einen web worker, Über http oder verarbeite genau dort.

Hoffe das macht Sinn.

96
ganaraj

Eine sehr interessante Frage! Ich finde die Web-Worker-Spezifikation etwas umständlich (wahrscheinlich aus guten Gründen, aber immer noch umständlich). Die Notwendigkeit, den Worker-Code in einer separaten Datei zu speichern, erschwert die Lesbarkeit eines Dienstes und führt Abhängigkeiten zu statischen Datei-URLs in Ihrem angular - Anwendungscode ein. Dieses Problem kann mithilfe der URL.createObjectUrl () behoben werden, mit der eine URL für eine JavaScript-Zeichenfolge erstellt werden kann. Auf diese Weise können wir den Worker-Code in derselben Datei angeben, die den Worker erstellt.

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

Die Web-Worker-Spezifikation hält auch den Worker- und den Haupt-Thread-Kontext vollständig getrennt, um Situationen zu verhindern, in denen Deadlocks, Livelocks usw. auftreten können. Es bedeutet aber auch, dass Sie im Arbeiter keinen Zugriff auf Ihre angular Dienste haben, ohne etwas herumzuspielen. Dem Worker fehlen einige Dinge, die wir (und Winkel) beim Ausführen von JavaScript im Browser erwarten, wie die globale Variable "document" usw. Durch "Verspotten" dieser erforderlichen Browserfunktionen im Worker können wir angular zu rennen.

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

Einige Funktionen funktionieren offensichtlich nicht, Bindungen an das DOM usw. Aber das Injection-Framework und zum Beispiel der $ http-Dienst funktionieren einwandfrei, was wir wahrscheinlich von einem Worker erwarten. Hierdurch erhalten wir den Vorteil, dass wir Standarddienste angular in einem Worker ausführen können. Wir können daher die im Worker verwendeten Services genau wie bei jeder anderen angular Abhängigkeit einem Komponententest unterziehen.

Ich habe einen Beitrag verfasst, der etwas mehr darüber ausführt hier und ein Github-Repo erstellt, das einen Service erstellt, der die oben diskutierten Ideen umsetzt hier

16
liket

Ich habe in Angular here ein voll funktionsfähiges Beispiel für Webworker gefunden

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

}]);

Es werden Versprechungen verwendet, um darauf zu warten, dass der Arbeiter das Ergebnis zurückgibt.

11
Octane

Angular Web Worker mit Polling-Beispiel

Wenn Sie mit den Arbeitern in AngularJS zu tun haben, ist es häufig erforderlich, dass Ihr Arbeiterskript inline ist (sofern Sie einige Build-Tools wie gulp/grunt verwenden), und wir können dies mit dem folgenden Ansatz erreichen.

Das folgende Beispiel zeigt auch, wie Abfragen an Server mit Workern durchgeführt werden können:

Zuerst erstellen wir unsere Arbeiterfabrik:

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

Rufen Sie als nächstes von unserem Controller die Arbeiterfabrik auf:

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

Sie können jederzeit myWorker.stopWork(); aufrufen, um den Worker von Ihrem Controller aus zu beenden!

Dies ist in IE11 + und FF und Chrome getestet

7
Chanakya Vadla

sie können sich auch angular plugin https://github.com/vkiryukhin/ng-vkthread ansehen

damit können Sie eine Funktion in einem separaten Thread ausführen. Grundsätzliche Verwendung:

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

Beispiele und API-Dokumentation: http://www.eslinstructor.net/ng-vkthread/demo/

--Vadim

2
vadimk