Multithreading in javascript: Introduction to Web Workers
Tags: javascript, programming, beginners, webdev
Published: 2023-08-14 08:36:45

As we all know javascript is single-threaded, meaning that it can only ever execute one piece of code at a time. When an asynchronous event occurs (like a mouse click, a timer firing, or an XMLHttpRequest completing) it gets queued up to be executed later. In other words, just single thread handles the event loop. While this design simplifies programming, it also introduces limitations, especially when tackling resource-intensive tasks. Computationally intensive tasks, causes applications to become unresponsive or slow. This is where the need for Web Workers becomes evident.

## Web Workers

Web Workers are browser-based threads that enable parallel execution of tasks, allowing heavy computations, data processing, and other resource-intensive operations to occur in the background. Workers run on a separate thread than the main execution thread. Data is sent between the main thread and workers through messages. This separation of tasks into multiple threads not only enhances performance but also maintains the responsiveness of the user interface.

### Setting Up Web Workers

setting up web workers is pretty easy. Lets get into it.

#### 1. Setting up project folder and adding HTML

create a project folder and add `index.html` in the root of it.
```html
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Web Workers</title>
        <script src="main.js"></script>
    </head>
    <body></body>
</html>
```

#### 2. create javascript file named `main.js` in the same folder

`main.js` contains the main code which will create new web workers and assign tasks to them. We will create an array of numbers from `1 to 200_000` and calculate sum of all previous numbers as we iterate through it just to mimic complex work. We will also measure the performance gains when using `1` worker vs when using `8` workers.
```js
const msToS = (ms) => {
    return Number(ms / 1000).toFixed(4);
};

const chunkify = (list, size) => {
    const chunks = [];

    for (let i = size; i > 0; i--) {
        chunks.push(list.splice(0, Math.ceil(list.length / i)));
    }

    return chunks;
};

const run = (work, workers) => {
    const chunks = chunkify(work, workers);

    const tick = performance.now();
    let completedWorkers = 0;

    console.clear();
    chunks.forEach((chunk, i) => {
        const worker = new Worker("./worker.js");
        worker.postMessage(chunk);

        worker.addEventListener("message", () => {
            console.log(`Worker ${i + 1} completed`);
            completedWorkers++;

            if (completedWorkers == workers) {
                console.log(
                    `${workers} workers took ${msToS(
                        performance.now() - tick
                    )} s`
                );
            }
        });
    });
};

const main = () => {
    const list = Array.from({ length: 200_000 }, (_, i) => i + 1);

    const workers = 1;
    run(list, workers);
};

main();
```

#### 3. create javascript file named `worker.js` in the same folder

This file contains the actual work to be done by web worker. We will add message event listener and pass an array (single chunk) from `run()` method in `main.js`.

```js
self.addEventListener("message", (e) => {
    const list = e.data;

    for (item of list) {
        let sum = 0;
        for (i = 0; i <= item; i++) {
            sum += i;
        }
    }

    self.postMessage("done");
    self.close();
});
```

### Running Web Workers

Let us start with single web worker and see how much time it takes to complete the work. Make sure `workers` variable is set to `1` in `run()` method in `main.js`. Run the project in any browser in open developer console. You will see that single worker took more than a minute to complete the work.

![single worker run](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m7fqp4345vk7uqn43jq6.png)

Now lets bump up the workers to `8`. set `workers` variable to `8` in `run()` method in `main.js`. It should look like this

```js
const workers = 8;
```

Run the project once more and observe the performance gains. You will see that `8` workers completed the work in about `18` seconds. This is about 72% performance gain than single worker. Isn't this amazing.

![8 workers run](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ub7r7kqw8x9nr0ekhayf.png)

Feel free to play with the code and learn :)