How can angularJS use web workers to run processes in the background? Is there any pattern I should follow on doing this?
Currently, I am using a service that has the model in a separate web worker. This service implements methods like:
ClientsFacade.calculateDebt(client1); //Just an example..
In the implementation, this method sends a message to the worker with the data. This allows me to abstract the fact that it is being performed in a separate thread and I could also provide an implementation that queries against a server or even one that does this action in the same thread.
Since I'm new to javascript and I'm just recycling knowledge I have from other platforms I wonder if this is something you would do or perhaps Angular which is what I am using, offers a sort of way of doing this. Also this introduces a change in my architecture since the worker must explicitly push changes to the controller, which then updates its values and then this is reflected in the view, am I over engineering this? It's a bit frustrating that web workers "protect" me so much from screwing up by not allowing me to share memory etc.
To add a web worker to an existing project, use the Angular CLI ng generate command. You can add a web worker anywhere in your application. For example, to add a web worker to the root component, src/app/app. component.
AngularJS is a JavaScript-based structural framework for developing dynamic web applications. Its importance can be best understood by the front-end web developers who have to rely most on the JavaScript to create the intuitive areas of a website that users see in their web browser.
Web Workers are a simple means for web content to run scripts in background threads. The worker thread can perform tasks without interfering with the user interface.
AngularJS is a structural framework for dynamic web applications. It lets you use HTML as your template language and lets you extend HTML's syntax to express your application components clearly and succinctly. Its data binding and dependency injection eliminate much of the code you currently have to write.
Communication with Web workers happens through a messaging mechanism. Intercepting these messages happens in a call back. In AngularJS, the best location to put a web worker is in a service as you duly noted. The best way to deal with this is to use promises, which Angular works amazingly with.
Here is an example of a webworker
in a 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;
}
};
});
Now whatever external entity that accesses Hello World service need not care about the implementation details of HelloWorldService
- HelloWorldService
could probably process the data over a web worker
, over http
or do the processing right there.
Hope this makes sense.
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"
], { type: 'application/javascript' }));
var worker = new Worker(blobURL);
The web worker spec also keeps the worker and main thread contexts completely separate to prevent situations were deadlocks and livelocks etc can occur. But it also means you won't have access to your angular services in the worker without some fiddling. The worker lack some of the things we(and angular) expect when executing JavaScript in the browser, like the global variable "document" etc. By "mocking" these required browser features in the worker we can get angular to run.
var window = self;
self.history = {};
var document = {
readyState: 'complete',
cookie: '',
querySelector: function () {},
createElement: function () {
return {
pathname: '',
setAttribute: function () {}
};
}
};
Some features obviously won't work, bindings to the DOM etc. But the injection framework and for example the $http service will work just fine, which is probably what we want in a worker. What we gain by this is that we can run standard angular services in a worker. We can therefore unit test the services used in the worker just as we would with any other angular dependency.
I made a post which elaborates a bit more about this here and created a github repo which creates a service which implements the ideas discussed above here
I found a fully working example of web workers in 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;
});
}]);
It uses promises to wait for the worker to return the result.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With