How to use Web Workers in Angular app

How to use Web Workers in Angular app

Learn about Web Workers and how to use them in your Angular app

Web apps would run better if heavy computations could be performed in the background, rather than compete with the user interface.

We need all apps to be very fast and highly responsive. Like lightning fast and responsive. So in that way, we have researched and found many ways of making our apps very fast, to keep our users. It is said humans are very impatient wait for an app to load or run some computations, we will leave a site if it takes ~3s to run/load. So check your analytics you may have a wonderful website but may have low visitors because of how performant (low or high) your app is, you need to check it today. In this post, we will discuss one of the optimization tricks to make our Angular apps highly performant.

Web Worker in general

Web Worker is really a worker, working its ass off because of the heavy load it is given. In computing context, Web Worker is a background thread that is created alongside/parallel the main thread where heavy computations are done to prevent drag on the main thread.

Web worker is one of the many workers we have in web. There are

All do the same thing, creating a background thread but run different functions. So the similar thing they do is create a parallel thread.

What is a thread? Whenever a program runs, it is allocated a space(memory address space) in RAM where its code and data are stored and executed by the CPU instruction by instruction one at a time. The memory space allocated is called the main thread. A thread is a piece of code that has its codespace inside a process. If a CPU supports multi-threading, it can run multiple threads at a time.

What is multi-threading? This is the concurrent execution/running of threads at the same time in a process. In Win, we create threads using the CreateThreadEx(...) API. This creates a codespace parallel to the main thread. The CPU executes both threads by executing one for a time saves its context, load the next thread in the registers and continues with its execution. This context switch happens on a scale of ~billionth(1/10^9) of a second that it appears to us to happen at the same time and very fast.

So in Worker, the JS engine creates a new thread and loads the JS script in the thread. In multi-threading, threads communicate with each other using shared resources. The shared resource is placed in a central place in the RAM where the threads can read/write to the resource thereby communicating with each other.

In the browser, we have the main thread which is called the DOM thread, during the loading of a new script in a tab, a DOM thread is created where the JS engine loads, parses and executes the JS files in the page. The Worker will create a new thread called the Worker Thread that will run a JS script parallel to the DOM thread. The JS script run by the Worker thread would not have a reference to the DOM because it is running in a different environment where no DOM APIs exists.

Using Web Worker

Like we said earlier, Workers don’t have access to the DOM APIs. They can’t access any of the following:

  • window object
  • document object

Web Workers can access:

  • navigator object
  • location object (read-only)
  • XMLHttpRequest
  • setTimeout(), clearTimeout(), setInterval(), clearInterval()
  • The atob() and btoa() functions

Web Workers can also access

  • Cache object

Web workers can run asynchronously and synchronously. Web Workers run their code from top to down then they enter into event loop, executing tasks/callbacks scheduled by event (APIs like setTimeout, setInterval, etc). First, they run synchronously executing the script then they enter asynchronous listening for events and executing them.

Web Worker provides methods/APIs that we can use to run our JS script. To instantiate a Worker, we run this:

const webWorker = new Worker('./script.js')

The JS script file is passed to the Worker when instantiating it. The Worker will create a new thread, parse and generate machine code from the JS script file, the machine code will be loaded in the new webWorker thread memory address space. Then the CPU will concurrently run the DOM thread and the webWorker thread.

Web Worker can send messages to the DOM thread that spawned it. It has several event listeners that trigger a registered callback when the event is fired.

onmessage

This is triggered when a message is received.

webWorker.onmessage = function(event) {
    // ...
 }

onerror

This is triggered when an error is thrown in the Worker.

webWorker.onerror = function(event) {
    // ...
 }

We can send messages using the postMessage API

From the DOM to the Web Worker

webWorker.postMessage(data)

From the Web Worker to the DOM thread:

postMessage(data)

We can listen for events using the addEventListener API

webWorker.addEventListener('message', function(evt) {
    // ...
 })

Using in Angular

It seems Web workers are used in vanilla JavaScript apps, most of us are addicted to Angular. We, Angular developers, have tried to use Web Workers in Angular but eh Webpack configuration and all proved very painful to setup. Up until the release of Angular CLI v8, using Web Workers in Angular, its setup, compiling, bundling and code-splitting was made easy by the CLI tool.

To generate a Web Worker, we run the ng g web-worker command:

ng g web-worker webworker

This will generate webworker.ts file in the src/app of an Angular app. The web-worker tells the CLI tools that the file would be used by a Worker.To demonstrate how to use Web worker in Angular to optimize its performance. Let’s say we have an app that calculates Fibonacci numbers. Finding Fibonacci numbers in the DOM thread will kinda impact the UI experience because the DOM and the user interactions would freeze until the number is found.Starting, our app would be like this:

// webWorker-demo/src/app/[email protected]({
    selector: 'app',
    template: `
        <div>
            <input type="number" [(ngModel)]="number" placeholder="Enter any number" />
            <button (click)="calcFib">Calc. Fib</button>
        </div>
        <div>{{output}}</div>
    `
})
export class App {
    private number
    private output
    calcFib() {
        this.output =fibonacci(this.number)
    }
}function fibonacci(num) {
    if (num == 1 || num == 2) {
        return 1
    }
    return fibonacci(num - 1) + fibonacci(num - 2)
}

Calculating Fibonacci numbers is recursive, passing small numbers like 0–900 would have no performance impact. Imagine passing ~10,000. That’s when we will begin to notice performance drag. Like we said the best bet is to move the fibonacci function or algorithm to execute in another thread. So no matter how large the number is, it will not be felt in the DOM thread.

So we scaffold a Web Worker file:

ng g web-worker webWorker

and move the fibonacci function into the file:

// webWorker-demo/src/app/webWorker.ts
function fibonacci(num) {
    if (num == 1 || num == 2) {
        return 1
    }
    return fibonacci(num - 1) + fibonacci(num - 2)
}self.addEventListener('message', (evt) => {
    const num = evt.data
    postMessage(fibonacci(num))
})

Now we will edit the app.component.ts to add Web Worker

// webWorker-demo/arc/app/[email protected]({
    selector: 'app',
    template: `
        <div>
            <input type="number" [(ngModel)]="number" placeholder="Enter any number" />
            <button (click)="calcFib">Calc. Fib</button>
        </div>
        <div>{{output}}</div>
    `
})
export class App implements OnInit{
    private number
    private output
    private webworker: Worker    ngOnInit() {
        if(typeof Worker !== 'undefined') {
            this.webWorker = new Worker('./webWorker')
            this.webWorker.onmessage = function(data) {
                this.output = data
            }
        }
    }    calcFib() {
        this.webWorker.postMessage(this.number)
    }
}

Our code is now 😘. We added ngOnInit lifecycle hook in our component so to initialize the Web Worker with the Web Worker file we generated earlier. We registered to listen to messages sent fro the Web Worker in the onmessage handler any data we get we will display it in the DOM.

We made the calcFib function to send the number to Web Worker. This below in webWorker would capture the number

self.addEventListener('message', (evt) => {
    const num = evt.data
    postMessage(fibonacci(num))
})

and processes the Fibonacci number then send the result back to the DOM thread. The onmessage we set up in the app.component

ngOnInit() {
        if(typeof Worker !== 'undefined') {
            this.webWorker = new Worker('./webWorker')
            this.webWorker.onmessage = function(data) {
                this.output = data
            }
        }
    }

would receive the result in datathen we will display the result in the DOM using the {{output}} interpolation.

During the processing of the Fibonacci numbers, the DOM thread would be left focusing on the user interactions while the webWorker would do the heavy processing.

Another Example: Prime Number

Let’s have a look at a simple example that calculates prime numbers:

// primes-demo/src/app/[email protected]({
    selector: 'app',
    template: `
        <div>
            <input type="number" [(ngModel)]="number" placeholder="Enter any number" />
            <button (click)="calcPrimes">Calc. Prime Numbers</button>
        </div>
        <div>{{output}}</div>
    `
})
export class App {
    private number
    private output    calcPrimes() {
        this.output = this.getPrimes(this.number)
    }    getPrimes(number) {
        if (typeof number == "number") {
            if(number<0){
                return "negative integers can not be prime";
            }
            if(number==0){
                return "zero is not a prime number";
            }
            if(number==1){
                return "1 is not a prime number";
            }
            var nonprimes = [],  // Array of non prime numbers
            var i,j,primes = []; // Array of prime numbers            for (i = 2; i <= number; ++i) {
                if (!nonprimes[i]) {
                    // i has not been marked -- it is prime
                    primes.push(i);
                    for (j = i << 1; j <= number; j += i) {
                        nonprimes[j] = true;
                    }
                }
            }
            return primes;  // Array of prime numbers
        }
        else{
            return "invalid input";
        }
    }
}

Number entered in the input box is held in the number property, when the Calc. Prime Numbers button is clicked the calcPrimesmethod is called which calls the getPrimes function, this contains the algorithm to calculate prime numbers of a number, this function returns an array that contains the number primes, then the calcPrimeassigns it to the output property which is displayed on the browser.

Prime number calculation can become expensive when the number increases, so prime number calculation is a good candidate to be offloaded to the Web Worker thread.

So we scaffold a new Web Worker file:

ng g web-worker primesWorker

Then we add the code to primesWorker:

// primes-demo/src/app/primesWorker.tsfunction getPrimes(number) {
    if (typeof number == "number") {
        if(number<0){
            return "negative integers can not be prime";
        }
        if(number==0){
            return "zero is not a prime number";
        }
        if(number==1){
            return "1 is not a prime number";
        }
        var nonprimes = [],  // Array of non prime numbers
        var i,j,primes = []; // Array of prime numbers        for (i = 2; i <= number; ++i) {
            if (!nonprimes[i]) {
                // i has not been marked -- it is prime
                primes.push(i);
                for (j = i << 1; j <= number; j += i) {
                    nonprimes[j] = true;
                }
            }
        }
        return primes;  // Array of prime numbers
    }
    else{
        return "invalid input";
    }
}self.addEventListener('message', (evt) => {
    const num = evt.data
    postMessage(getPrimes(num))
})

Then our app.component.ts will be rewritten to this:

// primes-demo/src/app/[email protected]({
    selector: 'app',
    template: `
        <div>
            <input type="number" [(ngModel)]="number" placeholder="Enter any number" />
            <button (click)="calcPrimes">Calc. Prime Numbers</button>
        </div>
        <div>{{output}}</div>
    `
})
export class App implements OnInit {
    private number
    private output
    private primesWorker: Worker    ngOnInit() {
        if(typeof Worker !== 'undefined') {
            this.primesWorker = new Worker('./primesWorker')
            this.primesWorker.onmessage = function(data) {
                this.output = data
            }
        }
    }    calcPrimes() {
        this.primesWorker.postMessage(this.number)
    }
}

The prime number calculation is now being done in another thread leaving the DOM thread free.The thing here is that the time it would take a heavy calculation to complete in the main thread is the same as in the Worker thread. Moving it off to the Worker thread doesn’t reduce the calculation speed, it just prevents the DOM thread from locking and becoming unresponsive.Terminating a workerAccording to Using Web Worker APIs — Wikipedia

If you need to immediately terminate a running worker from the main thread, you can do so by calling the worker’s terminate method:

webWorker.terminate();

The worker thread is killed immediately.In our Angular example, we didn’t kill the Worker thread. When the app.component.tsis destroyed the Worker thread will still be left open and be hanging around. This is very bad practice, we should clean up the Worker thread when we are done with it.To do so, we will utilize the ngOnDestroyhook in Angular. This hook is what Angular calls when a component is being destroyed, so we will terminate the Worker thread there.

// primes-demo/src/app/[email protected]({
    selector: 'app',
    template: `
        <div>
            <input type="number" [(ngModel)]="number" placeholder="Enter any number" />
            <button (click)="calcPrimes">Calc. Prime Numbers</button>
        </div>
        <div>{{output}}</div>
    `
})
export class App implements OnInit, OnDestroy {
    private number
    private output
    private primesWorker: Worker    ngOnInit() {
        if(typeof Worker !== 'undefined') {
            this.primesWorker = new Worker('./primesWorker')
            this.primesWorker.onmessage = function(data) {
                this.output = data
            }
        }
    }    calcPrimes() {
        this.primesWorker.postMessage(this.number)
    }    ngOnDestroy() {
        his.primesWorker.terminate()
    }
}

In the fibonacci example:

// webWorker-demo/arc/app/[email protected]({
    selector: 'app',
    template: `
        <div>
            <input type="number" [(ngModel)]="number" placeholder="Enter any number" />
            <button (click)="calcFib">Calc. Fib</button>
        </div>
        <div>{{output}}</div>
    `
})
export class App implements OnInit, OnDestroy {
    private number
    private output
    private webworker: Worker    ngOnInit() {
        if(typeof Worker !== 'undefined') {
            this.webWorker = new Worker('./webWorker')
            this.webWorker.onmessage = function(data) {
                this.output = data
            }
        }
    }    calcFib() {
        this.webWorker.postMessage(this.number)
    }    ngOnDestroy() {
        this.webWorker.terminate()
    }
}

Conclusion

In this post, we saw what Web Worker is, its APIS and how to use it. Further down, we saw how to use the Angular CLI to easily add Web Worker to our Angular apps and we saw its usage in Angular on how we moved our Fibonacci calculation to the Worker thread.

In as much Worker is good in offloading our work to the Worker thread, we should also try to write optimizable code in the Worker script because it would take the same time in the main thread. So adding Worker to a good JS code would be lit, your app would be blazingly fast 🚀.

If you have any question regarding this or anything I should add, correct or remove, feel free to comment, email or DM me.

Web Development Services

Web Development Services

As one of the best Web Application Development Company, it provides a fine quality mobile app development service at an affordable price. Especially, it encourage start-ups that have unique ideas, by offering a more competitive price

HireFullStackDeveloperIndia is rated as one of the top Web Application Development Company in India by various industry magazines and review sites. They have a right blend of award-winning designers, expert programmers and Google certified digital marketers which make them a unique one-stop solution for hundreds of our clients, spread across all countries.

A Good website reflects not only your business but also it is one of the main factors why a potential customer would convert into Client. A good website design helps increase traffic driving leads to grow business. The best web design company create a custom design for each corporate website so as to help them meet their business goals.

Get Quote: https://hirefullstackdeveloperindia.com/get-a-quote/

Web Development Services in USA

Web Development Services in USA

HireFullStackDeveloperIndia goal is to get the app downloaded millions of times, generate revenue and featured on app stores.

HireFullStackDeveloperIndia is a leading Web Application Development Company in USA that mission is simple: To help our clients see what they cannot see themselves. We work alongside elite brands to uncover what we believe to be simple truths: that all roads lead to web app. That they should dream bigger. And most importantly, that they’re underestimating how an amazing mobile experience can revolutionise the way they interact with their customers.

Visit us: What is the best Full Stack Development Company in Nevada, USA?

Mobile App Development Company India | Ecommerce Web Development Company India

Mobile App Development Company India | Ecommerce Web Development Company India

Best Mobile App Development Company India, WebClues Global is one of the leading web and mobile app development company. Our team offers complete IT solutions including Cross-Platform App Development, CMS & E-Commerce, and UI/UX Design.

We are custom eCommerce Development Company working with all types of industry verticals and providing them end-to-end solutions for their eCommerce store development.

Know more about Top E-Commerce Web Development Company