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.
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
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
nis called trial division. This method divides
nby each integer from 2 up to the square root of
n. Any such integer dividing
nevenly 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:
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
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.
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
primeworker if it is,
- Directly calls the
- Returns the created subject.
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.
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:
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)).
ChangeDetectorRef.detectChanges() method must be called when the state of the component is updated asynchronously as we use the
OnPush change detection strategy.
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!