In a previous blog post we identified a potential performance issue caused by calling a method from the template of an Angular component and saw that time-consuming operations could still be a problem even for an optimized application.

Using the same use case - a very simple application consisting of a number input that compute the next prime number -, we will now study how to externalise long-running operations to a web worker.
Web workers makes it possible to run a script operation in a background thread separate from the main execution thread of a web application. The advantage of this is that laborious processing can be performed in a separate thread, allowing the main (usually the UI) thread to run without being blocked/slowed down.
Generating a Web-Worker with Angular
Angular CLI provides a very convenient way to generate a usable web-worker for an existing application.
The following command generates a file named prime.worker.ts
:
|
|
Its content is simple, this web worker only returns the given data as a response:
|
|
The Angular CLI command also generates the following code to call the web-worker:
|
|
The configuration file angular.json
is updated and a tsconfig.worker.json
file is generated.
All you need to do is to restart the application and start using the created web-worker!
As it is, this web-worker does not help our prime-number computation problem. We need to extract the logic and use it in the generated web-worker.
Extracting the prime number logic
In the previous version of our application, we used a synchronous Angular service to compute:
isPrime
: tells if the given number is prime,nextPrime
: compute the next prime number.
|
|
Both these operations can take some time as we use the most basic computational method: trial division.
The most basic method of checking the primality of a given integer
n
is called trial division. This method dividesn
by each integer from 2 up to the square root ofn
. Any such integer dividingn
evenly establishes n as composite; otherwise it is prime.
We need to externalise this logic in a static Object, so we can call it from the previously generated web-worker (or directly from a service if web-workers are not available, for example when using Server-side rendering).
Let’s create a file named prime-static.ts
with the same methods as our PrimeService
but here defined as static methods:
|
|
The isPrime
and nextPrime
methods are similar to the previous version.
We added the primeOperation
method that can dynamically call the other two depending on the operation: PrimeOperation
parameter.
This method is used in the updated prime-worker.ts
web-worker:
|
|
So, depending on the given data, this web-worker will either compute the next prime value or tell if the current one is a prime.
Now let’s create another version of the PrimeService
that uses this web-worker!
Using the Web-Worker in an async service
We call it PrimeAsyncService.
This service being asynchronous, it returns Observables instead of raw values.
These observables send a next value when the prime-worker
web-worker has finished its task and posted the answering message.
The file prime-async.service.ts
:
|
|
The callWorker()
function is an update of the code generated by Angular to call the web-worker:
- It declares a subject,
- Checks if web-workers are available,
- Calls the
prime
worker if it is, - Directly calls the
PrimeStatic
object otherwise, - Returns the created subject.
When the prime
worker sends a message, it sets the next value of the subject.
The caller of the method can subscribe to it and be notified asynchronously with the operation result.
Stateful CallWorkerComponent
We can now create the CallWorkerComponent
component that uses this service to display a number input form, a button to get the next prime number, and text that tells if the current value is a prime number.
Here is a screenshot of such component in loading the state:

Two class fields are used in the HTML template for the component state: loading
and isPrime
.
Remember from our previous blog post that methods should not be called from Angular template to avoid performance drawbacks.
|
|
We also use the OnPush
change detection strategy to apply all tricks learned until now to maximize the performances of our application :
|
|
This component listens for changes made to the numberInput with this.numberForm.valueChanges
and calls the async PrimeAsyncService with the pipe mergeMap(number => this.prime.isPrime(number))
.
The ChangeDetectorRef.detectChanges()
method must be called when the state of the component is updated asynchronously as we use the OnPush
change detection strategy.
The method computeNextPrimeNumber
is called when the user clicks on the Next button.
Here again it uses the PrimeAsyncService to get the next prime number value asynchronously using the underlying web-worker.
Web-Worker performance test
Let’s check what are the benefits in terms of performances!
The Angular Devtools Profiler is the proper tool to know how long it takes to Angular for reacting to a click on the Next button and computing the next prime number value.
It shows us that it takes only 1ms to handle the click itself:

And 0.2ms to update the component state once the web-worker has done computing the next prime value:

So we can conclude that using a web-worker has successfully prevented our Angular application from freezing even while doing an operation that can take several seconds of computing!