Web Worker in Angular | better programming

How to Improve Application Performance and User Experience with Web Workers in Angular

Photo by Johannes Planio: https://www.pexels.com/photo/selective-focus-photography-of-two-men-builder-figurines-1445324/

This article is about – you guessed it – web workers in Angular.

Web workers allow us to execute JavaScript from the main thread. We will present how they can improve application performance and user experience.

More threads means more things are done at the same time. But with great power comes great responsibility. We need to manage and synchronize threads.

Looks like we have a few points to cover.

let’s get started!

Angular is a TypeScript-based framework for building single-page web applications. TypeScript is a superset of JavaScript.

But, as you already know, browsers can only understand JavaScript. Therefore, everything in an Angular project must be “transpiled” into JavaScript.

JavaScript is a single-threaded language; Thus, any application built with Angular. Everything runs on a single thread, the so called main thread.

A single thread can do only one job at any given moment. This can be problematic when running computationally expensive commands.

Imagine our application needs to perform heavy calculations or render large charts. At some point, the application freezes while the main thread struggles to do all the work.

Nonsense Is there any way to avoid this and improve the user experience?

Enter Web Worker!

The MDN documentation states, “Web workers are a simple means of running scripts for web content in background threads.”

Web workers are separate threads that can run scripts in the background without interfering with the main thread. They can run independently without affecting the performance of the page.

Most modern browsers support web workers in their latest versions.

It looks very promising. Let’s see them in action!

Our demo application has a page that calculates, routed CalculationComponent,

It has an input field that receives a number. Based on the number typed by the user, it calculates. well sort of.

Below is the template file. For the sake of simplicity, we omit the CSS classes. You can find the full snippet version in the GitHub repository link at the end of this article.

respectively, we have the class file. Whenever the user types a number, modelChange method is called, which in turn calls calculate function with newValue (line 13).

we will use calculate serves in two places, so we declare it in a separate file (more on that soon).

ok, you got us!

For our demo, we don’t do any real calculations. Instead, we create an artificial delay by looping for N seconds, where N is the number the user has typed.

Problem

Let’s see how it looks.

Notice how the page freezes for two seconds?

The larger the number, the longer the UI becomes unresponsive (which is true for many real calculations). During that time, the user cannot interact with the page.

Another issue you may have missed is that the “Calculating…” label was never displayed. The main thread was so busy that he couldn’t manage to present this transformation.

Here is where web workers come in handy.

create a web worker

We create a web worker by running the following command in the terminal:

ng g web-worker calculation

It does a few things for us:

  1. it makes a calculation.worker.tsin which we will take the call forward calculate function, that is, what is blocking the main thread.
  2. it makes a tsconfig.worker.json file, which is the worker configuration.
  3. This option adds "WebWorkerTsConfig: "tsconfig.worker.json" In angular.json file.

calculation.worker.ts The file initially contains the scaffold code, as seen below:

source of delay calculate Celebration. Thus, we need to pass its call to the worker.

Note that we import calculate separate function calculation.ts The file we talked about earlier.

You must be wondering do we need a separate file for this? Can’t we declare function in component class file and import it from there?

As it turned out, the answer is no. we can not do.

If we did, Angular would have thrown a bunch of compilation errors indicating that there is no DOM typing available in the web worker’s compilation unit.

This is because one of the main limitations of web workers is that they do not have access to the DOM.

As per this comment, “If you import a function from a library, the entire library must be compatible with the current compilation type, be it Web Worker, Browser, or Node.js.”

In our case, the “Library” component is the class file. Obviously it’s not meant to compile without DOM typing, like, say, in a web worker, hence the errors.

using web worker

Next, we need to modify our current implementation to use Web Workers.

Web workers and key threads can communicate through messages and send data.

Web worker API provides a postMessage method of sending a message to a worker and a onmessage Event handlers for receiving and responding to messages.

  • Along the lines of 22-24, we are going to introduce a new . make up Worker,
  • On line 25, we set isCalculating To true so that the label “Calculating…” is displayed.
  • On line 26, we use postMessage Method to call worker and pass input value from main thread.
  • Along lines 27-30, we assign an anonymous function onmessage Web worker’s event handler. Upon receiving the result, we assign it to the class variable result and then set isCalculating To false,

having a backup plan

Murphy’s law states, “If something can go wrong, it will.”

In our case, this translates to the user’s browser (or environment) that does not support web workers. That’s why we need a fallback solution for such cases.

On line 12, we check whether web workers are supported. If they are not, we call calculate Tasks from the main thread. We could also display a message to give users a heads-up.

This is far from ideal.

Another option is to split the computation into pieces, execute a piece of the computation, then use setTimeout To schedule the next run to run at some later point. This will free the main thread for some time and prevent it from blocking.

We are not done yet!

Web worker solved our problem. Moving the workload to a background thread allows the main thread to respond to user interaction.

Sadly, having multiple threads creates a different kind of problem.

Let’s review the demo application again.

The user typed “3”, then changed his mind, cleared the field, and added “1.” typed.

Typing “3” creates thread A which will respond in three seconds. Typing “1” creates thread B which will respond in a second. Thread B will respond first and display its result. So far, so good.

After a few seconds, thread A will also respond and replace the previous result. After all, the visible result is from thread A, not thread B!

When using multiple threads, we may have to deal with all kinds of problems that multithreading brings, such as concurrency and synchronization issues. In general, these issues are difficult to deal with.

In our case, the solution is very simple. Before creating a new thread, we eliminate the previous one (row 12). We can do this because we always keep a reference to the current worker (lines 21-24).

You can find the code in this GitHub repository.

In this article, we talked about Web Workers in Angular.

Use web workers to perform time-consuming calculations in the background. But don’t mindlessly use them for things that are already fast and non-blocking. Keep in mind that multithreading doesn’t come for free. This can introduce synchronization issues in your application.

In our next article, we’ll explore how we can render charts using Web Workers and discuss some of their major limitations in detail.

Thanks for reading. Stay tuned for more.

Leave a Comment