Web Workers with AngularJS
To incorporate Web Workers into an AngularJS application, we’ll create a service. With a service, we can reuse the same worker in multiple controllers throughout the application. Services are also a convenient place for isolating processing that happens outside the context of AngularJS.
Below is a live example of Web Workers in an AngularJS application. Below the Plunker, I explain the key parts of the application.
The worker is defined inside the web-worker.js file. To conserve memory when passing messages to the worker, we stringify the message body. This means the worker must first parse the message data (line 7). In this implementation, directly after parsing the message, we get a reference to the unique identifier of the message (line 9). We uniquely identify each message in case multiple clients are calling the worker with different data.
Since the worker runs on a different thread from its client, the client cannot directly call a function inside the worker. To facilitate multiple, independent functions inside the worker, this worker contains a switch statement that uses the second parameter of the message to determine which worker function to call (data on line 11).
Finally, the worker calls the matching function and passes the data in need of processing (data on line 21). When the worker finishes processing, it stringifies the processed data and posts it back to the client with the MESSAGE_ID.
Web Worker Service
In web-worker.service.js, we expose a public method for calculating the compound annual growth rate (CAGR) from an array of values. This service establishes a reference to the worker (line 13) and posts messages to the worker inside the calculateCompoundAnnualGrowthRate function.
To keep track of calculations from different callers within the application, the calculateCompoundAnnualGrowthRate function passes a MESSAGE_ID parameter to the worker and back to its caller. The MESSAGE_ID is a unique identifier generated from the UUID Service.
As discussed in the worker code explanation, the second parameter is the name of the worker function we want to call, in this case, calculateCompoundAnnualGrowthRate. The third parameter is the data we need processed.
In the activate function of the service, we establish a listener for the worker object. This listener will call the onWebWorkerResponse handler whenever the worker indicates it has finished processing some data by posting a message.
Inside the onWebWorkerResponse function, we get the message identifier and the processed data, and use AngularJS’ $broadcast function to let any listeners know we have new data.
One thing to note — since the broadcast event does not trigger a digest cycle, we need to use the $apply function in order for values updated by the broadcasted data to update their bindings in the views.
Inside the activate function of app.controller.js, we ask the Web Worker Service to calculate the CAGR of the resolved data (fetched before initializing the controller in app.config.js). In this example, the resolved data is five years of home values. The Web Worker Service passes back a message identifier with which we can listen for a response once the data finishes processing. The listener is established on line 25.
When the listener receives a response, it calls the controller’s onMessageReceived function. This function updates the CAGR property in the view (home.html), formatting it as a percent in the process (line 29). Finally, we deregister the listener, since we don’t expect any more messages.