AngularJS und Webworker

Lesezeit: 7 Minuten

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

Derzeit verwende ich einen Dienst, der das Modell in einem separaten Webworker enthält. 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. Dadurch kann ich die Tatsache abstrahieren, dass dies in einem separaten Thread ausgeführt wird, und ich könnte auch eine Implementierung bereitstellen, die einen Server abfragt, oder sogar einen, der diese Aktion im selben Thread ausführt.

Da ich neu bei Javascript bin und nur Wissen recycele, das ich von anderen Plattformen habe, frage ich mich, ob Sie dies tun würden, oder vielleicht bietet Angular, das ich verwende, eine Art Möglichkeit, dies zu tun. Auch dies führt zu einer Änderung in meiner Architektur, da der Worker Änderungen explizit an den Controller übertragen muss, der dann seine Werte aktualisiert und dies dann in der Ansicht widerspiegelt. Bin ich überdimensioniert? Es ist ein bisschen frustrierend, dass Webworker mich so sehr vor Fehlern “schützen”, indem sie mir nicht erlauben, Speicher zu teilen usw.

Benutzer-Avatar
Ganaraj

Die Kommunikation mit Web-Workern erfolgt über einen Messaging-Mechanismus. Das Abfangen dieser Nachrichten geschieht in einem Rückruf. In AngularJS ist der beste Ort, um einen Web-Worker zu platzieren, ein Dienst, wie Sie bereits bemerkt haben. Der beste Weg, damit umzugehen, ist die Verwendung von Versprechen, mit denen Angular erstaunlich gut funktioniert.

Hier ist ein Beispiel für eine webworker in einem service

var app = angle.module("meineApp",[]);  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;
        }
    };

});

Now whatever external entity that accesses Hello World service need not care about the implementation details of HelloWorldServiceHelloWorldService could probably process the data over a web worker, over http or do the processing right there.

Hope this makes sense.

  • what is doWork.js in var worker = new Worker(‘doWork.js’); ?

    – achudars

    Aug 8, 2013 at 16:26


  • It is a reference to the external js file that contains the code for the web-worker.

    – ganaraj

    Aug 8, 2013 at 16:30

  • yeah, is it possible to use services like $http in doWork.js?

    – iLemming

    Sep 8, 2014 at 23:44

  • If doWork is called again before the worker finishes, won’t defer be overwritten? The second promise would then resolve with the first result, and the first promise would never resolve.

    – hughes

    Jan 16, 2015 at 2:40

  • @hughes From my read of the code, yes, I think you are right. This code needs to be modified to provide a new defer to resolve each time doWork(data) is called.

    – ryanm

    Dec 29, 2015 at 21:45

A very interesting question! I find the web worker specification a bit awkward (probably for good reasons, but still awkward). The need to keep the worker code in a separate file makes the intention of a service hard to read and introduces dependencies to static file URLs in your angular application code. This problem can be mitigated by using the URL.createObjectUrl() which can be used to create a URL for a JavaScript string. This allows us to specify the worker code in the same file which creates the worker.

var blobURL = URL.createObjectURL(new Blob([
    "var i = 0;//web worker body"
], { Typ: 'Anwendung/Javascript' }));  var worker = neuer Worker (blobURL);

Die Web-Worker-Spezifikation hält auch den Worker- und den Main-Thread-Kontext vollständig getrennt, um Situationen zu verhindern, in denen Deadlocks und Livelocks usw. auftreten können. Aber es bedeutet auch, dass Sie ohne etwas Fummelei keinen Zugriff auf Ihre Winkeldienste im Worker haben. Dem Worker fehlen einige der Dinge, die wir (und eckig) erwarten, wenn wir JavaScript im Browser ausführen, wie die globale Variable „document“ usw. Indem wir diese erforderlichen Browserfunktionen im Worker „mocken“, können wir eckig zum Laufen bringen.

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

Einige Funktionen werden offensichtlich nicht funktionieren, Bindungen an das DOM usw. Aber das Injection-Framework und zum Beispiel der $http-Dienst werden gut funktionieren, was wahrscheinlich das ist, was wir in einem Worker wollen. Was wir dadurch gewinnen, ist, dass wir Standardwinkeldienste in einem Worker ausführen können. Wir können daher die im Worker verwendeten Dienste genau so testen, wie wir es mit jeder anderen Winkelabhängigkeit tun würden.

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

Benutzer-Avatar
Oktan

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

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 verwendet Versprechungen, um darauf zu warten, dass der Worker das Ergebnis zurückgibt.

Benutzer-Avatar
Chanakya Vadla

Angular Web Worker mit Polling-Beispiel

Wenn Sie mit den Workern in AngularJS zu tun haben, ist es oft erforderlich, dass Ihr Worker-Skript inline ist (falls Sie einige Build-Tools wie gulp/grunt verwenden), und wir können dies mit dem folgenden Ansatz erreichen.

Das folgende Beispiel zeigt auch, wie die Abfrage auf dem Server mithilfe von Arbeitern durchgeführt werden kann:

Lassen Sie uns zuerst unsere Arbeiterfabrik erstellen:

    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 an:

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

Du kannst anrufen myWorker.stopWork(); jederzeit den Worker von Ihrem Controller kündigen!

Dies wird in IE11+ und FF und Chrome getestet

Sie können sich auch das Winkel-Plugin ansehen https://github.com/vkiryukhin/ng-vkthread

wodurch Sie eine Funktion in einem separaten Thread ausführen können. Grundlegende 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-Dokument: http://www.eslinstructor.net/ng-vkthread/demo/

– Wadim

  • das ist fantastisch!!

    – MattE

    11. März 2018 um 20:46 Uhr

  • @vadimk Ich erhalte diesen Fehler bei der Verwendung Ihres Plugins! vkThread ist keine Funktion

    – DevLoverUmar

    2. August 2019 um 7:55 Uhr


  • das ist fantastisch!!

    – MattE

    11. März 2018 um 20:46 Uhr

  • @vadimk Ich erhalte diesen Fehler bei der Verwendung Ihres Plugins! vkThread ist keine Funktion

    – DevLoverUmar

    2. August 2019 um 7:55 Uhr


1094510cookie-checkAngularJS und Webworker

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy