AngularJS and web workers

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.


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