Using Web Worker API In Your Reactjs Project To Enhance Performance
Tags: webdev, javascript, react
Published: 2024-04-24 12:42:42

What is a web worker? 

This is a question beginners learning frontend often ask. Sometimes, they can't wrap their head around reasons for using the web worker API and when it should be used.

Web Workers in simple terms, are a means to run a set of instructions in the background. Why background? Javascript was created in 1995 to run small bits of tasks on the web browser. Javascript is designed to run non-concurrently (i.e. run one task at a time) on a single main thread. Web Worker makes it possible to create another thread called the worker thread to run tasks, without interfering with the user interface.

Developers use Web Worker API to ease the burden of tasks on the main thread. Running memory-intensive tasks like implementing autosave functionality (for writing web apps, e.g. Google Docs), can affect user interactions and cause lagging. Web worker makes it possible to assign those tasks to another thread.

![diagram to show how web workers work](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88mute9kf7v76r6xoevj.png) _Left: Without web worker, Right: With web worker_

Using web worker in cases like the one mentioned above helps to;

- improve the responsiveness and avoid freezing of the web app,
- improve the overall performance of the web app, and
- better make use of the CPU resources by utilizing multiple threads

## Real-World Use Cases of Web Workers
Web worker is used in web applications that have the following features:

1. **Image processing:** For web apps with features like image resizing, filtering, manipulation and facial recognition. Web workers can be used for complex canvas manipulations or image rendering in the background.
2. **Video encoding and decoding:** Developers use web workers in streaming apps to process video files for playback or manipulation without freezing the user interface.
3. **Real-time applications:** Real-time applications that use socket.io for fetching data. Web apps like messaging platforms, and crypto dashboard shows the latest price of cryptocurrencies in a chart.
4. **Background Tasks:** Web workers can handle tasks that don't require immediate visual updates on the page. Examples include syntax highlighting in online code editors. It is also used to fetch large amounts of backend API data.

## Web Worker API
There are two types of web workers:

### Shared Worker
The shared worker is accessible to multiple pages and scripts. The shared worker is spawned by calling the `SharedWorker()` constructor.

```
const worker = new SharedWorker('worker.js')
```

### Dedicated Worker
A dedicated worker is only accessible to the script that called it. This way, only the page that called it can communicate with it.
A dedicated worker is created by calling the `Worker()` constructor. The constructor takes the script's path to run in the worker thread as a parameter.

```
const worker = new Worker('worker.js')
```

## Communication with a dedicated worker
To send a message to and from a dedicated worker, `postMessage()` is used. The `onmessage` method is for listening to messages.

The `postMessage()` takes in the data or message to be sent as a parameter. The data can be any javascript object, with exemptions like functions and DOM nodes. This is because data is copied between the main page and the worker, not shared. The page and worker do not share the same instance, i.e. duplicate result is created on each side.

## Terminating web worker
To terminate a running web worker from the main thread, the `terminate()` method is used.

## Web Worker Limitations
While web worker API is a powerful tool to improve performance, there are a few limitations to keep in mind when working with them.

- Web workers do not have access to all window objects, e.g. `window.location`,
- It can not directly manipulate the DOM. 🙅‍♂️  No `document.addEventListener()`

Checkout list of functions and classes available to web workers: [https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers)

## Implementing web worker in Reactjs applications
To better understand the concept of how web workers work, let's consider an example in a Reactjs app.

To imitate a memory-intensive task, we will create a function that returns the [factorial](https://en.wikipedia.org/wiki/Factorial) of a random number from 0–100.

```
//Memory-intensive calculation

export default function calcFactorial() {
const randomNum = Math.floor(Math.random() * 100);
let factorial = 1;
if (randomNum === 0) {
return 1;
}
for (let i = 1; i <= randomNum; i++) {
factorial *= i;
}
return factorial;
}
```

To create a web worker, we need a `js` file (e.g. worker.js). In this file, we listen to messages from `app.js` using the `onmessage` method. The `message.data` from the parameter is a boolean that determines if the calculation should start or stop. If `message.data` is true, we run `calcFactorial()` and send the returned value to `app.js` every 2 seconds.

```
//worker.js

import calcFactorial from "../utils/randomNum";
onmessage = function (message) {
const init = message.data;
if (init) {
setInterval(() => {
const randomFactorial = calcFactorial();
postMessage(randomFactorial);
}, 2000);
} else {
return;
}
};
```
In `app.js`, we initialize a dedicated worker and communicate with the `worker.js` file using `postMessage()` and `onmessage` methods.

```
import { useState, useEffect } from "react";

function App() {

//initialize web worker
const worker = new Worker(new URL("./worker/worker.js", import.meta.url), {
type: "module",
});

const [initWorker, setInitWorker] = useState(false);
const [randomFactorial, setRandomFactorial] = useState(0);

useEffect(() => {

//communicates with the worker file
if (initWorker) {
worker.postMessage(true);
}
worker.onmessage = function (e) {
setRandomFactorial(e.data);
};

return (()=>{
worker.terminate()
})
}, [initWorker, randomFactorial]);

const handleClick = () => {
setInitWorker(!initWorker);
};

return (
<div>
<h1>Web worker example</h1>
<div className='randomNumBox'>
<h2>Random Factorial: {randomFactorial}</h2>
<button onClick={handleClick}>{initWorker ? "Stop" : "Start"}</button>
</div>
</div>
);
}
export default App;
```

## Result

![result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ckem61be4d4ndlzawfxf.gif)

[https://github.com/psalmuelle/andika-tutorial/tree/worker](https://github.com/psalmuelle/andika-tutorial/tree/worker)

## Conclusion
In this article, we explored the web worker API and how it is being used to improve web performance. Here are additional resources to know more about web worker API:

- [https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)
- [https://www.w3schools.com/html/html5_webworkers.asp](https://www.w3schools.com/html/html5_webworkers.asp)