Using Web Workers to Handle Heavy Tasks in the Browser

Introduction In modern web applications, performance is a critical factor that directly affects the user experience. One of the main causes of lagging or UI blocking is due to heavy tasks being executed directly on the main thread. To address this issue, JavaScript provides the Web Workers API, which allows running code in parallel in a separate thread, helping avoid blocking the main thread and improving application performance. 1. What are Web Workers? Web Workers are a browser API that allows you to execute JavaScript in a separate thread from the main application thread. Web Workers can communicate with the main thread via postMessage. Ideal for handling: heavy computation, data analysis, image processing, file compression, etc. 2. Why use Web Workers? Benefits: No UI blocking: since it doesn't run on the main thread. Better UX: the UI remains responsive while the task is being processed. Improved performance for complex calculations. Without Web Workers: The UI can easily freeze when processing large JSON files, long loops, or complex algorithms. Users may think the app crashed if there's no response. 3. Example: Using Web Workers to Calculate Factorial Suppose you need to calculate factorial (n!) of large numbers — this can be a task that slows down or freezes the UI if processed on the main thread. Web Workers help offload this task to a separate thread, ensuring a smooth UI. We’ll build a simple example including: A Web Worker file to calculate factorial. A React (TSX) component with an input box, a button to send data to the worker, and tags to display the result. factorialWorker.ts – Worker file self.onmessage = function (e) { const number = e.data; const result = factorial(number); self.postMessage(result); }; function factorial(n: number): BigInt { let result = BigInt(1); for (let i = 2; i { const [input, setInput] = useState(100000); const [result, setResult] = useState(null); const [loading, setLoading] = useState(false); const [totalTime, setTotalTime] = useState(0); const workerRef = useRef(null); const handleCalculate = () => { setResult(null); setTotalTime(0); setLoading(true); const startTime = performance.now(); // Start timing const worker = new Worker(new URL("@src/worker/factorialWorker", import.meta.url)); workerRef.current = worker; worker.postMessage(input); worker.onmessage = (e: MessageEvent) => { const endTime = performance.now(); // End timing setTotalTime(endTime - startTime); // Calculate total time in milliseconds setResult(e.data); setLoading(false); worker.terminate(); }; }; return ( setInput(Number(e.target.value))} placeholder="Enter a positive integer" /> {loading ? "Calculating..." : "Calculate Factorial"} {result !== null && Result: {result}} {totalTime > 0 && Calculation time: {totalTime.toFixed(3)} ms} ); }; export default FactorialCalculator; Result: Without Web Worker With Web Worker As you can see, using a Web Worker results in a much smoother UI compared to not using one. 4. Notes When Using Web Workers You cannot access the DOM from inside a worker file. Make sure postMessage communication is clear and avoids race conditions. You can use Comlink to simplify communication. 5. When Should You Use Web Workers? Scenario Should You Use a Worker? Heavy computation like encryption, file compression ✅ Yes Processing large JSON files (>1MB) ✅ Yes Simple UI updates ❌ No Fetch API, server calls ❌ No (just use async/await

May 7, 2025 - 11:05
 0
Using Web Workers to Handle Heavy Tasks in the Browser

Introduction

In modern web applications, performance is a critical factor that directly affects the user experience. One of the main causes of lagging or UI blocking is due to heavy tasks being executed directly on the main thread.

To address this issue, JavaScript provides the Web Workers API, which allows running code in parallel in a separate thread, helping avoid blocking the main thread and improving application performance.

1. What are Web Workers?

Web Workers are a browser API that allows you to execute JavaScript in a separate thread from the main application thread.

  • Web Workers can communicate with the main thread via postMessage.
  • Ideal for handling: heavy computation, data analysis, image processing, file compression, etc.

2. Why use Web Workers?

Benefits:

  • No UI blocking: since it doesn't run on the main thread.
  • Better UX: the UI remains responsive while the task is being processed.
  • Improved performance for complex calculations.

Without Web Workers:

  • The UI can easily freeze when processing large JSON files, long loops, or complex algorithms.
  • Users may think the app crashed if there's no response.

3. Example: Using Web Workers to Calculate Factorial

Suppose you need to calculate factorial (n!) of large numbers — this can be a task that slows down or freezes the UI if processed on the main thread. Web Workers help offload this task to a separate thread, ensuring a smooth UI.

We’ll build a simple example including:

  • A Web Worker file to calculate factorial.
  • A React (TSX) component with an input box, a button to send data to the worker, and

    tags to display the result.

factorialWorker.ts – Worker file

self.onmessage = function (e) {
  const number = e.data;
  const result = factorial(number);
  self.postMessage(result);
};

function factorial(n: number): BigInt {
  let result = BigInt(1);
  for (let i = 2; i <= n; i++) {
    result *= BigInt(i);
  }
  return result;
}

export {};

FactorialCalculator.tsx – React component

import React, { useState, useRef } from "react";

const FactorialCalculator: React.FC = () => {
  const [input, setInput] = useState<number>(100000);
  const [result, setResult] = useState<number | null>(null);
  const [loading, setLoading] = useState(false);
  const [totalTime, setTotalTime] = useState(0);
  const workerRef = useRef<Worker | null>(null);

  const handleCalculate = () => {
    setResult(null);
    setTotalTime(0);
    setLoading(true);
    const startTime = performance.now(); // Start timing

    const worker = new Worker(new URL("@src/worker/factorialWorker", import.meta.url));
    workerRef.current = worker;

    worker.postMessage(input);

    worker.onmessage = (e: MessageEvent<number>) => {
      const endTime = performance.now(); // End timing
      setTotalTime(endTime - startTime); // Calculate total time in milliseconds

      setResult(e.data);
      setLoading(false);
      worker.terminate();
    };
  };

  return (
    <div>
      <input
        type="number"
        value={input}
        onChange={(e) => setInput(Number(e.target.value))}
        placeholder="Enter a positive integer"
      />
      <button onClick={handleCalculate} disabled={loading}>
        {loading ? "Calculating..." : "Calculate Factorial"}
      button>
      {result !== null && <p>Result: {result}p>}
      {totalTime > 0 && <p>Calculation time: {totalTime.toFixed(3)} msp>}
    div>
  );
};

export default FactorialCalculator;

Result:

Without Web Worker With Web Worker
Without Web Worker With Web Worker

As you can see, using a Web Worker results in a much smoother UI compared to not using one.

4. Notes When Using Web Workers

  • You cannot access the DOM from inside a worker file.
  • Make sure postMessage communication is clear and avoids race conditions.
  • You can use Comlink to simplify communication.

5. When Should You Use Web Workers?

Scenario Should You Use a Worker?
Heavy computation like encryption, file compression ✅ Yes
Processing large JSON files (>1MB) ✅ Yes
Simple UI updates ❌ No
Fetch API, server calls ❌ No (just use async/await

This site uses cookies. By continuing to browse the site you are agreeing to our use of cookies.