Sitemap
Avyaan Verma

Avyaan Verma

Full-stack dev who ships fast and breaks things slightly less often now. I am TS/JS Dev.

Web Workers Explained

9 min readSep 1, 2026

--

Is JavaScript really single-threaded? Not quite.

Ever run an expensive calculation in the browser and watched everything freeze, buttons stop responding, scrolling lags, the whole page just locks up? That’s JavaScript’s single-threaded nature biting you. In this blog, we’ll break down how the browser handles JavaScript on a single thread, then dive into Web Workers: what they are, how to use them, how to bring them into a React app, a hands-on particle simulation project to see them in action, and — as a bonus — a look at Service Workers on the backend side.

To understand web workers in javascript we need to understand first how javascript actually works.

Have you ever wanted to open a cloud kitchen ?

Honestly I have always wanted top open a cloud kitchen because the concept of making food and eating all of it by myself always intrigued me. But seriously if you want to open a kitchen you would be cooking everything all by yourself.

Let’s say your bussiness is going super well you are getting orders all the time, but the problem is if you are more than one orders at the same time. Isn’t it all too much for just one person or chef isn’t it ? To solve this problem what would you do ?

Obviously — get more chefs — could be your friends or your family or you can just hire more chefs. But more importantly you need more people. So now you aren’t just the main chef you have other chefs who are all preparing seperate orders.

Javascript is just like you, the main chef of the browser, who is just a single chef and that’s why we call it a single threaded language.

Is JavaScript Really Single-Threaded? (The Event Loop Explained)

Javascript is a single threaded which means that it executes the code line by line in a sequence. It executes all the operations line by line on a single thread.

Javascript works in a single execution context ( an execution context is where all the code gets executed ), taking in use of the call stack in which the functions and operations are pushed and when their execution is finished they are popped out.

It’s the same when you have a single chef working all alone which we can see in the below example.

Press enter or click to view image in full size
A single chef i.e. Javascript
const order1 = document.querySelector("#order1");
const order2 = document.querySelector("#order2");
const order3 = document.querySelector("#order3");

order1.addEventListener("click", ()=>{
console.log("Order1 prepared");
})

order2.addEventListener("click", ()=>{
console.log("Order2 prepared");
})

order3.addEventListener("click", ()=>{
console.log("Order3 prepared");
})

What Happens When You Block the Main Thread

Now Imagine you got two orders at the same time that you have to prepare , in that case even if the chef starts another order preparation it will be stuck and it won’t be able to work on any other order until that particular order gets finished. And if the order takes time it will be another problem.

See for yourself in the below example

Press enter or click to view image in full size
Example of multiple orders
const order1 = document.querySelector("#order1");
const order2 = document.querySelector("#order2");
const order3 = document.querySelector("#order3");

order1.addEventListener("click", ()=>{
console.log("Order1 prepared");
})

order2.addEventListener("click", ()=>{
for( let i=0; i<10000000000; i++){};
console.log("Order2 prepared");
})

order3.addEventListener("click", ()=>{
console.log("Order3 prepared");
})

Here in the example you might not notice but I have already pressed the order two but until that order2 is getting prepared i can’t preapre any other order until that particular order2 get’s finished and at that point of time my Chef or so to say my Browser is freezed. My UI is inaccessible until JS prepares this order.

This ruins our user experience because a performance of the web application is extremely important for a user in a browser which in turn ranks your website.

If we go into a bit of technicality this is happening because JS is a single threaded which means that it gets executed in a single execution environment which is getting executed in a main stack.

Press enter or click to view image in full size
Main stack gets blocked

To solve this problem don’t you think we wither need a different stack or more exactly a different thread ?

Well here’s when the topic workers come in.

What Are Web Workers?

Web Workers are a browser API that let’s you run Javascript in a background thread apart from main execution thread that we usually run our code in. By the help of workers we are able to run multiple tasks in parallel to the main execution thread thereby not blocking or slowing down our main thread.

The advantage we get from this is that all of the heavy working that we can do get offloaded to the background script i.e. the worker we create.

Press enter or click to view image in full size

Your browser under the hood works using threads, and every tab that you see which has been opened at this point is working on single thread.

Crazy right ?

To solve the above problem we needed atleast a single thread and there can be definately situations where we may required more than one thread. Just like in the example we need multiple chefs we required multiples threads to execute our code and to free the main load.

How to Create and Use a Web Worker (Step-by-Step)

To create a Worker in your code you need to call the Worker contructor and create a single worker.

/* main.js */
const worker1 = new Worker("./worker.js");

This creates worker thread and tells JS to execute the worker.js script in that thread/context.

Definately you can create multiple workers as well.

/* main.js */
const worker1 = new Worker("./worker.js");
const worker2 = new Worker("./worker.js");
const worker3 = new Worker("./worker.js");
/* worker.js */
function prepareOrder2(){
for( let i=0; i<10000000000; i++){};
console.log("Order2 prepared");
}

You can write the task inside the worker script you want to execute.

Get Avyaan Verma’s stories in your inbox

Join Medium for free to get updates from this writer.

The worker thread doesn’t have access to the DOM just like the main thread and the main thread and the worker thread cannot directly communicate with each other. To fix that we use something known as postMessage() API.

Web Workers vs Main Thread — How They Communicate (postMessage)

/* main.js */
order2.addEventListener("click", () => {
let message = "Order2";
worker.postMessage(message);
});

First we send a message from our main thread to the worker thread.

/* worker.js */
function prepareOrder2() {
for (let i = 0; i < 10000000000; i++) {
// heavy computation
}
console.log("Order2 prepared");
}

self.onmessage = (event) => {
if (event.data === "prepare") {
prepareOrder2();
self.postMessage("Order 2 prepared");
}
};

In the worker script we have self object which refers to the global object ( no not the window object ), which can also omit the self altogether as well. The Event OnMessage in the worker thread when it comes from the main thread then we prepare the order2 on the worker thread.

After the order or the heavy computation gets completed we send the message back to the main thread.

order2.addEventListener("click", () => {
let message = "Order2";
worker.postMessage(message);
});

worker.onmessage = (event) => {
console.log(event.data);
};
Press enter or click to view image in full size

Web Workers vs Asynchronous Programming ( Parallelism vs Concurrency )

You must be thinking that isn’t this problem solved by asyncronous programming ? Well no not exactly asynchronous programming asks you to wait for the particular thing and continue on with your other tasks while you work on your main thread.

Web workers are executed on a seperate thread unlike asynchronous programming.

Let me give you a simpler example to understand this.

Javascript is a single threaded language that works on single execution stack which is why any I/O operation such as network operation, geolocation, microphone access, camera access, etc. require Javascript to use the Web APIs which when completed are put into the different queues. If it’s a promise it moves into micro queues and if its something like setTimeout or setInterval it moves into the task queues.

Asynchronous programing is basically having a single chef but having multiple assistants to do jobs like chopping, shopping, ingredients or packing and stuff. It helps you achieve concurrency which is basically having a single thread but not waiting for any I/O operation.

Web workers allow us to achieve multithreading in JS through which we can have multiple chefs apart from our main chef. This means we can have JS run in different execution contexts. This is proceess is known as parallelism where multiple threads are working in parallel.

Crazy Web Worker Use Cases (Heavy Computation, Data Processing, Parsing)

Web Workers offers some crazy usecases, some are very common but some are very interesting. Here are some examples where web workers are used such as scientific simulations, machine learning inference, real-time graphics, audio processing, cryptography, and large computational algorithms.

Also it’s very important to understand that it’s not about computing a lot of data but keeping the computation local while making the browser responsive.

1. Machine Learning in the Browser

Assuming you want to train an AI model like RNN model inside any browser, instead of just sending all data to the server you directly run it using Web workers and see the result as well. If you have a task like image classification, face detection, speech recognition, text embeddings that require millions of mathematical operations we can do it very easily Using Web workers

2. Scientific Simulations

There are scientific applications like particle physics planets molecules fluid dynamics and if you are into that stuff where you have to constantly update velocity take care of direction and calculate forces off not just one but thousands of objects you can use Web Focus to simulate without freezing those animations or user interactions.

3. Image & Video Processing

Editing photos or processing live camera frames involves millions of pixel calculations. Operations like background removal, object detection, filters, and image enhancement are computationally expensive. A Web Worker processes each frame in the background while the main thread continues displaying the interface and camera preview.

4. Procedural Generation

Games and creative tools often generate terrain, cities, textures, or worlds using mathematical algorithms instead of storing them. These generation algorithms can take hundreds of milliseconds or even seconds. Web Workers create new chunks of the world in the background so users can continue exploring without interruptions.

Using Web Workers in React

Now comes the real part where you will be using web workers, React.js is popular User Interface library that is used almost everywhere and maintaining smooth web performance is crucial for a web application and if you are dealing with heavy computation web workers can come really handy.

Let’s see an example on how to use web workers in a React app.

/* App.tsx */
import { useEffect, useState } from "react";

const App = () => {
const [n, setN] = useState(0);
const [result, setResult] = useState(0);

useEffect(() => {
console.log("inside useEffect")
const newWorker = new Worker(
new URL("./workers/fibonacciWorker.js", import.meta.url),
);

newWorker.onmessage = (event)=>{
setResult(event.data);
}

newWorker.postMessage(n);

return () => {
newWorker.terminate();
}

}, [n]);
return (
<div>
<div className="fibonacci">
<h1>Calculate Fibonacci Series</h1>
<label htmlFor="number">Number: </label>
<input type="number" id="number" onChange={(e) => setN(Number(e.target.value))} value={n} />
<h3>Result: {result}</h3>
</div>

<div className="bgChange">
<button onClick={()=> {
console.log("hello")
}}>print logs</button>
</div>
</div>
);
};
export default App;
/* fibonacciNo.ts */
function fibonacci(n : number) : number {
if (n <= 1) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}

onmessage = ((event)=>{
const n = event.data;
const result = fibonacci(n);
postMessage(result);
})

Conclusion

Web Workers are very useful when in some heavy computation your main thread gets blocked, and you want to offload your blocking code into other threads in your CPU.

While building a web application, web performance of it is what differentiates a high ranking website to a website that nobody wants to open. This unlocks a whole new field of web application building that you might see online like 3D websites or a model in a browser.

References I Took and you can read online

--

--

Avyaan Verma
Avyaan Verma

Written by Avyaan Verma

Full-stack dev who ships fast and breaks things slightly less often now. I am TS/JS Dev.