Skip to Content
Course content

93: Web Workers for Background Processing

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're running a small cafe. You're the only employee. You take the orders, you pour the coffee, and you also bake the elaborate, five-layer cakes. For a while, it works. But then, a customer orders a cake that takes three hours to bake. Because you're the only one there, you have to stay in the kitchen for those three hours. While you're baking, the front door is locked, the phone is ringing unanswered, and the other customers are staring at you through the window, wondering why the shop has effectively ceased to exist.

In this scenario, you are the JavaScript main thread. The cafe is your browser tab. When you run a massive calculation—like processing a 50MB CSV file or calculating prime numbers—you're "baking the cake." Because JavaScript is single-threaded, the UI freezes. The buttons don't click, the animations stop, and the user thinks your app has crashed.

Web Workers are how we hire a chef. You stay at the front counter handling the customers (the UI), and you send a ticket to the chef in the back (the Worker). The chef does the heavy lifting in a separate thread, and when the cake is done, they ring a bell to let you know it's ready to be served.

The Cost of Being Single-Threaded

I've seen plenty of juniors try to solve UI freezing by using setTimeout or async/await. Here is the hard truth: async/await doesn't actually run code in parallel. It just manages when things happen. If you have a loop that runs a billion times, it doesn't matter if it's inside an async function; it will still block the main thread and freeze your page. That's where Web Workers come in. They provide a truly separate execution context.

Offloading the Heavy Lifting

To use a Worker, you need a separate file. You can't just define the worker function inside your main script because the browser needs to spawn a completely new OS-level thread for it. Let's say we want to find the sum of a massive array of numbers without locking up our "Submit" button.

First, we create our worker file, calculator.js:

// calculator.js
self.onmessage = function(e) {
  const data = e.data; // This is the array we sent from the main thread
  console.log('Worker: Starting heavy calculation...');
  
  // Simulate a heavy task
  const result = data.reduce((acc, num) => acc + num, 0);
  
  // Send the result back to the main thread
  self.postMessage(result);
};

Now, in your main JavaScript file, you instantiate the worker and set up the communication channel:

// main.js
const myWorker = new Worker('calculator.js');

const bigData = Array.from({ length: 10000000 }, () => Math.floor(Math.random() * 100));

// Send the data to the worker
myWorker.postMessage(bigData);

// Listen for the result
myWorker.onmessage = function(e) {
  console.log('Main Thread: The worker finished! Result is: ' + e.data);
};

console.log('Main Thread: I am still responsive! I can handle clicks and scrolls.');

The postMessage Handshake

Notice that the main thread and the worker don't share memory. You can't just reach into the worker and change a variable, and the worker can't touch the DOM. This is a safety feature—if they both tried to change the same piece of data at once, you'd end up with "race conditions," which are a nightmare to debug.

Instead, they communicate via postMessage(). Think of it like sending a letter. You pack your data into an envelope, send it off, and wait for a reply. When you call postMessage, the browser actually clones the data you're sending. For most objects, this is fine, but if you're moving massive amounts of data (like raw image pixels), you'll eventually want to look into "Transferable Objects," which move the data instead of copying it. But for 95% of use cases, the standard cloning is exactly what you need.

One last tip: remember to terminate your workers when you're done with them using myWorker.terminate(). If you keep spawning workers without killing them, you'll leak memory and eventually slow the user's entire computer to a crawl.




📋 Practical Task

Build a Non-Blocking Prime Number Finder

Your goal is to create a page that calculates whether a very large number is prime without freezing the browser's UI.

  • The Interface: Create an HTML page with an input field for a number, a "Check Prime" button, and a loading spinner (a simple CSS animation or just a text element saying "Calculating...").
  • The Worker: Create a primeWorker.js file. It should receive a number and run a loop to check for primality. To make the "blocking" effect obvious, use a very large prime or a very large odd number.
  • The Integration:
    • When the button is clicked, the main thread should show the loading spinner and postMessage the number to the worker.
    • The main thread must remain responsive (try adding a counter button on the page that increments a number; it should keep working while the prime is being calculated).
    • When the worker returns the result, hide the spinner and display whether the number was prime.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.