AngularJS en webworkers

Hoe kan angularJS webworkers gebruiken om processen op de achtergrond uit te voeren? Is er een patroon dat ik moet volgen om dit te doen?

Momenteel gebruik ik een service die het model in een aparte webworker heeft. Deze service implementeert methoden zoals:

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

In de implementatie stuurt deze methode een bericht naar de werknemer met de gegevens. Dit stelt me ​​in staat om het feit te abstraheren dat het in een aparte thread wordt uitgevoerd en ik zou ook een implementatie kunnen bieden die een query uitvoert op een server of zelfs een die deze actie in dezelfde thread uitvoert.

Omdat ik nieuw ben met javascript en ik alleen kennis hergebruik die ik van andere platforms heb, vraag ik me af of dit iets is dat je zou doen of dat Angular, wat ik gebruik, een manier biedt om dit te doen. Dit introduceert ook een verandering in mijn architectuur, aangezien de werknemer expliciet wijzigingen naar de controller moet pushen, die vervolgens de waarden bijwerkt en dit wordt weerspiegeld in de weergave, ben ik dit te veel aan het engineeren? Het is een beetje frustrerend dat webwerkers me zo “beschermen” tegen het verknoeien door me niet toe te staan ​​om geheugen te delen, enz.


Antwoord 1, autoriteit 100%

Communicatie met webwerkers vindt plaats via een berichtenmechanisme. Het onderscheppen van deze berichten gebeurt in een terugbelverzoek. In AngularJS is de beste locatie om een ​​webwerker in een service te plaatsen, zoals u naar behoren heeft opgemerkt. De beste manier om hiermee om te gaan, is door gebruik te maken van beloften, waar Angular geweldig mee werkt.

Hier is een voorbeeld van een webworkerin een 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;
        }
    };
});

Welke externe entiteit die toegang heeft tot de Hello World-service, hoeft zich niet druk te maken over de implementatiedetails van HelloWorldServiceHelloWorldServicezou de gegevens waarschijnlijk kunnen verwerken via een web worker, via httpof voer de verwerking daar uit.

Ik hoop dat dit logisch is.


Antwoord 2, autoriteit 17%

Een zeer interessante vraag! Ik vind de specificatie van de webwerker een beetje onhandig (waarschijnlijk om goede redenen, maar nog steeds onhandig). De noodzaak om de werkcode in een apart bestand te bewaren, maakt de intentie van een service moeilijk leesbaar en introduceert afhankelijkheden van statische bestands-URL’s in uw hoekige toepassingscode. Dit probleem kan worden verholpen door de URL.createObjectUrl() te gebruiken die kan worden gebruikt om een ​​URL voor een JavaScript-tekenreeks te maken. Dit stelt ons in staat om de werknemercode te specificeren in hetzelfde bestand dat de werknemer aanmaakt.

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

De web worker-specificatie houdt ook de worker-context en de hoofdthread-context volledig gescheiden om situaties te voorkomen waarin deadlocks en livelocks enz. kunnen optreden. Maar het betekent ook dat u geen toegang hebt tot uw hoekige services in de worker zonder enig gehannes. De werker mist enkele van de dingen die we (en hoekig) verwachten bij het uitvoeren van JavaScript in de browser, zoals de globale variabele “document” enz. Door deze vereiste browserfuncties in de werker te “spotten” kunnen we hoekig krijgen om te draaien.

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

Sommige functies zullen duidelijk niet werken, binden aan de DOM enz. Maar het injectie-raamwerk en bijvoorbeeld de $ HTTP-service zal prima werken, wat waarschijnlijk wat we willen in een werknemer. Wat we hiermee winnen is dat we standaard hoekige diensten in een werknemer kunnen uitvoeren. We kunnen daarom eenheid testen van de diensten die in de werknemer worden gebruikt, net zoals we met een andere hoekafhankelijkheid zouden doen.

Ik heb een bericht gemaakt dat een beetje meer over deze hier heeft gemaakt en een GitHub-repo gecreëerd die een Service die de ideeën die hierboven besproeit hier

hier is


Antwoord 3, Autoriteit 11%

Ik vond een volledig werkvoorbeeld van webarbeiders in hoekige 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;
    });
}]);

Het gebruikt beloften om te wachten op de werknemer om het resultaat te retourneren.


Antwoord 4, Autoriteit 8%

hoekige webarbeider met polling voorbeeld

Wanneer u te maken hebt met de werknemers in Angularjs, is het vaak nodig dat uw werknemersscript inline is (Incase U gebruikt wat build-tools zoals GULP / GRUND) en we kunnen dit bereiken met behulp van de volgende aanpak.

Voorbeeld hieronder laat zien hoe polling kan worden gedaan naar server met behulp van werknemers:

Laten we eerst onze werknemer fabriek maken:

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

Vervolgens van onze controller bel de arbeidersfabriek:

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

Je kunt op elk moment myWorker.stopWork();aanroepen om de worker van je controller te beëindigen!

Dit is getest in IE11+ en FF en Chrome


Antwoord 5, autoriteit 2%

je kunt ook de hoekige plug-in bekijken https://github.com/vkiryukhin/ng-vkthread

waarmee je een functie in een aparte thread kunt uitvoeren.
basisgebruik:

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

Voorbeelden en API-document: http://www.eslinstructor.net/ng-vkthread/ demo/

–Vadim

Other episodes