Check out myAngular article series with live demos and Facebook group Angular - Advanced Topics

web workers in angular

Torgeir "Tor" Helgevold
- JavaScript Developer and Blogger
Published: Sun Jul 31 2016

One really cool thing about Angular is that the framework is decoupled from the DOM via a higher level API. In the following post I will show how to take advantage of this by running an Angular application in a web worker.

Configuring an Angular application to run as a web worker requires a little bit more setup code than a regular browser application. In this article I will go through a simple example that you can build on to make more complex applications later.

I will start by creating a simple component. In my sample I have created a component for calculating n-factorial. The idea is that a value n will be passed to the component from the outside. Then, the component will calculate n-factorial based on n and return the result.

If we look at the component code in isolation it's not really that different from regular components.

The main difference is the platformWorkerAppDynamic().bootstrapModule(WorkerModule) call that allows us to bootstrap the application in a web worker.

import {Component} from '@angular/core'; const FACTORIAL_CHANNEL = "FACTORIAL"; import {NgModule} from '@angular/core'; import {WorkerAppModule} from '@angular/platform-webworker'; import {platformWorkerAppDynamic} from '@angular/platform-webworker-dynamic'; import {ServiceMessageBrokerFactory, PRIMITIVE} from '@angular/platform-webworker'; @Component({ selector: 'app', template:'<div>Web worker loaded</div>' }) class Worker { time: string; constructor(private _serviceBrokerFactory: ServiceMessageBrokerFactory){ var broker = _serviceBrokerFactory.createMessageBroker(FACTORIAL_CHANNEL, false); broker.registerMethod("factorial", [PRIMITIVE], this.calculate, PRIMITIVE); } private calculate(val: string) { if(val) { let result = factorial(parseInt(val)); return Promise.resolve(result); } return Promise.resolve(''); } } @NgModule({imports: [WorkerAppModule], bootstrap: [Worker], declarations: [Worker]}) class WorkerModule { } platformWorkerAppDynamic().bootstrapModule(WorkerModule); function factorial(num) { if (num === 0) { return 1; } else { return (num * factorial(num - 1)); } }

There is also a ServiceMessageBrokerFactory at play here to facilitate web worker communication. Web worker communication is message based, so whenever we receive a message over the correct channel, the calculate method will be called. The calculate method is registered with the broker based on meta data with information about its input and output types. The PRIMITIVE symbol means the method requires primitives types like string, number and boolean as input and output.

The message broker expects the calculate method to return a promise

Next I have created a loader (loader.ts) to load the component we just created.

declare var System: any; importScripts( "node_modules/systemjs/dist/system.js", "node_modules/zone.js/dist/zone.min.js", "node_modules/reflect-metadata/Reflect.js", "config.js"); System.import("./web-worker-app.js");

Finally we create the entry point for the worker (main.ts)

import {PlatformRef} from '@angular/core'; import {UiArguments, FnArg, PRIMITIVE, ClientMessageBrokerFactory} from '@angular/platform-webworker'; import {bootstrapWorkerUi} from "@angular/platform-webworker"; const FACTORIAL_CHANNEL = "FACTORIAL"; bootstrapWorkerUi("factorial/loader.js") .then((ref: any) => { let brokerFactory: ClientMessageBrokerFactory = ref.injector.get(ClientMessageBrokerFactory); var broker = brokerFactory.createMessageBroker(FACTORIAL_CHANNEL, false); document.getElementById("calculate_factorial").addEventListener("click", (e) => { var val = (document.getElementById("factorial")).value; var args = new UiArguments("factorial"); args.method = "factorial"; var fnArg = new FnArg(val, PRIMITIVE); fnArg.value = val; fnArg.type = PRIMITIVE; args.args = [fnArg]; broker.runOnService(args, PRIMITIVE) .then((res: string) => { document.getElementById("result").innerHTML = `${res}`; }); }); });

In main.ts I kick of the loading of the web worker, but I also define a channel for communication between the host application and the web worker.

Once the worker application is bootstrapped I create a message broker to create a two way channel between the host and the web worker. Through this channel I can pass in the value 'n' and get the calculated factorial value in return.

The setup in the post bootstrap method matches the message broker definitions in the web worker component.

This was just a taste of what's possible with web workers in Angular 2.0, but I have added a demo here if you are interested.

As always, the code is also available on Github

If you liked this article, share it with your friends on social media:

We also have a new Facebook group about advanced Angular topics.

I invite you to follow me on twitter