techStackGuru

JavaScript Web Workers


A web worker is a feature in web development that allows scripts to run in background threads separately from the main execution thread. As a result, web applications perform faster and more responsively, especially for tasks like complex calculations, I/O operations, or other CPU-intensive operations.

The following example illustrates the use of JavaScript web workers:

worker.js

function fibonacci(n) {
    if (n <= 1) {
      return n;
    } else {
      return fibonacci(n - 1) + fibonacci(n - 2);
    }
  }
  
  onmessage = function(event) {
    const number = event.data;
    const result = fibonacci(number);
    postMessage(result);
  };

index.html

<!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Workers Example</title>
  </head>
  <body>
    <button id="calculate">Calculate Fibonacci</button>
    <p id="result"></p>
  
    <script>
      const worker = new Worker('worker.js');
  
      const calculateButton = document.getElementById('calculate');
      const resultElement = document.getElementById('result');
  
      calculateButton.addEventListener('click', () => {
        const number = 40; // Change this to the desired number
        worker.postMessage(number);
      });
  
      worker.onmessage = (event) => {
        const result = event.data;
        resultElement.textContent = `Fibonacci of ${number} is: ${result}`;
      };
    </script>
  </body>
  </html>

worker.js: It defines a function fibonacci that calculates the Fibonacci sequence, as well as a listener for the onmessage message. A worker calculates the Fibonacci of a message received and sends its result back to the caller using the postMessage method.

index.html: The file defines event listeners and creates a worker instance. A worker will receive the Fibonacci number when the "Calculate Fibonacci" button is clicked. When the worker has responded, a page displays its results.

Key points:

  • As long-running tasks are handled by web workers, the main thread remains responsive.
  • The main thread communicates with the worker via postMessage.
  • It is not possible for web workers to access the DOM directly, so communication and manipulation of the DOM must be handled separately.

  • Example 2: Image-worker

    The example shows how web workers can offload computationally intensive tasks such as image processing to a separate thread, which prevents the main thread from freezing.

    image-worker.js

    function grayscale(imageData) {
        const data = imageData.data;
        for (let i = 0; i < data.length; i += 4) {
          const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
          data[i] = data[i + 1] = data[i + 2] = avg;
        }
        return imageData;
      }
      
      onmessage = function(event) {
        const imageData = event.data;
        const processedData = grayscale(imageData);
        postMessage(processedData);
      };

    index.html

    <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Image Processing with Web Workers</title>
      </head>
      <body>
        <img id="original-image" src="image.jpg" alt="Original Image">
        <img id="processed-image" alt="Processed Image">
      
        <script>
          const worker = new Worker('image-worker.js');
      
          const originalImage = document.getElementById('original-image');
          const processedImage = document.getElementById('processed-image');
      
          originalImage.addEventListener('load', () => {
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            canvas.width = originalImage.width;
            canvas.height = originalImage.height;
            ctx.drawImage(originalImage, 0, 0);
            worker.postMessage(ctx.getImageData(0, 0, canvas.width, canvas.height));
          });
      
          worker.onmessage = (event) => {
            const processedData = event.data;
            processedImage.width = processedData.width;
            processedImage.height = processedData.height;
            const ctx = processedImage.getContext('2d');
            ctx.putImageData(processedData, 0, 0);
          };
        </script>
      </body>
      </html>
  • image-worker.js: There is also an onmessage event listener defined in this file, which converts pixels in an image to grayscale. Upon receiving an image data object, the worker applies the grayscale function and sends back the processed data.
  • index.html: The file creates an instance of a worker and loads an image. An image is loaded, and its data is sent to a worker. As a result of the worker responding, the processed image data is drawn onto a separate canvas and displayed.