DEV Community

Cover image for #LearnedToday: Web Workers
Daniel Zotti
Daniel Zotti

Posted on • Originally published at danielzotti.it

#LearnedToday: Web Workers

Web Workers are a powerful feature in modern web development, enabling JavaScript to run concurrently in the background without blocking the main user interface thread.

In the traditional web development model, JavaScript code executes on the same thread as the user interface. This means that computationally intensive tasks, such as complex calculations, data processing, or network requests, can significantly slow down the user experience. Users may encounter unresponsive web pages or even complete freezes when a script monopolizes the CPU.

Web Workers address this problem by introducing a multi-threaded approach to JavaScript execution within the browser. With them, you can create separate threads that run concurrently alongside the main application thread.

Key points to understand Web Workers

  • File: Web Workers are just JavaScript files loaded by the main thread.

  • Concurrency: Web Workers provide true concurrency by executing JavaScript code in parallel.

  • Isolation: Each Web Worker runs in isolation from the main thread and other workers. They have their own global scope, which means they cannot directly access variables or functions defined in the main thread or other workers.

  • Communication: Communication between workers and the main thread is achieved through message passing (postMessage), ensuring data integrity and safety.

  • Background Processing: Web Workers are ideal for offloading resource-intensive operations like data parsing, image processing, or lengthy computations to prevent them from affecting the user experience.

  • No UI Access: Web Workers are unable to access the Document Object Model (DOM) directly, as they run outside the main thread. This limitation ensures that UI operations remain exclusive to the main thread, preventing potential conflicts and race conditions.

  • Browser Support: Web Workers are widely supported in modern browsers, making them a viable technology for a broad range of web applications. However, it's essential to check browser compatibility when implementing a Worker in your projects.

Ok, too much chat... Let's dive into the code as usual!

Web Worker support

First of all. We need to make sure that the browser supports Web Workers. We do this by checking if the Worker instance is present in the window object:

// index.js
if (!window.Worker) {
  console.log(`Your browser doesn't support Web Workers!`);
  return;
}
Enter fullscreen mode Exit fullscreen mode

Web Worker creation

To use a Web Worker, simply create a new instance of a Worker, passing the URL of the associated file as an argument:

// index.js
const worker = new Worker('./worker.js');
Enter fullscreen mode Exit fullscreen mode

Prepare the communication with the Web Worker

To "connect" main thread with the Web Worker we need to listen to the messages the Web Worker might send using its exposed methods:

// index.js
worker.onerror = (error) => {
  // when an error occurs in the worker
};

worker.onmessage = (event) => { 
  // everytime a worker use `postMessage` to send a message
}

worker.onmessageerror = (event) => { 
  // e.g. the message from the worker can't be serialized
}
Enter fullscreen mode Exit fullscreen mode

Communication

In order to communicate with the web worker from the main thread, we just push a message using the postMessage method of the Worker:

// index.js
worker.postMessage({ message: 'Do something'});
Enter fullscreen mode Exit fullscreen mode

...and inside the worker we listen to the message and read the data property:

// worker.js
addEventListener('message', function (event) {
  console.log(event.data); // { message: 'Do something'}
})
Enter fullscreen mode Exit fullscreen mode

We can do the other way round by calling the postMessage inside the worker:

// worker.js
postMessage({ message: 'A message from the worker!'});
Enter fullscreen mode Exit fullscreen mode

...and react to the message event in the main thread using the onmessage method of the worker instance:

// index.js
worker.onmessage = (event) => { 
  console.log(event.data); // { message: 'A message from the worker!'}
}
Enter fullscreen mode Exit fullscreen mode

The whole code

Let's recap the basic code for the two files.

Main thread

// index.js
if (!window.Worker) {
  console.log(`Your browser doesn't support Web Workers!`);
  return;
}

const worker = new Worker('./worker.js');

worker.onerror = (error) => { ... };

worker.onmessage = (event) => { ... }

worker.onmessageerror = (event) => { ... }

worker.postMessage({ message: 'Do something'});
Enter fullscreen mode Exit fullscreen mode

Web Worker

// worker.js
addEventListener('message', function (event) {
  if(event.data === 'Do something') {
    postMessage({message: `Ok, I'll do!`});
  } else {
    console.log(`I won't do anything...`);
  }
})
Enter fullscreen mode Exit fullscreen mode

NB: postMessage is the only way the Web Worker can comunicate with the main thread and viceversa!

Demo

And as always, can't miss the demo and open source project on GitHub!

The project guides the user through a series of steps to follow, and the idea is to show the difference between a long-running task performed by the main thread (which blocks the UI) and one performed by a web worker.

NB: Performances are the same but with the Web Worker the UI is still usable.

Web Workers Playground preview

To simulate a long-running task I used this simple code:

function longTask(max) {
    let sum = 0;
    for (let i = 0; i < 5_000_000_000; i++) {
        sum += i;
    }
    return sum;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)