DEV Community

Linas Spukas
Linas Spukas

Posted on

Intro to Web Workers and Multithreading

JavaScript in the browser is a single-threaded environment, meaning it can run only one script at the same time. If you do heavy calculations and DOM manipulation at the same time, the browser will likely throw you a message about the unresponsive page and offer you to kill the script that takes a long time to finish.
To make the user experience better and escape blocking, we can use web workers. It is a system that spawns required worker to execute script in the background threads without interfering with the user interface.

To spawn a new worker, we need to call a constructor Worker() and pass URI to the script file, where new worker thread will be created and code executed. A good practice to check if a browser supports workers and handle error:

if (window.Worker) {
  const myWorker = new Worker('worker.js');
} else {
  console.log('Browser does not support web workers');
}

Worker Scope

A worker will run in a different context from the current window and trying to access it without keyword self will throw an error. Using self will reference the window context.
In a worker, you can run mostly any JavaScript code including navigotor object, WebSockets, data storage, XMLHttpRequest, extensive set of window methods or load other scripts. But you cannot directly manipulate the DOM.

Communication With a Dedicated Worker

Communication between the main page and worker is happening through method postMessage and event handler onmessage.
For example, to send data to a worker, we call a method:

// main js file
const myWorker = new Worker('worker.js');
myWorker.postMessage('Giggity');

Passed data between the main page and workers is copied, not shared, and can be of any type or Javascript object

To handle received data in the worker, we need to declare an event handler. The worker has access to event handler and method, and can be set up directly calling them:

// worker.js file
onmessage = (event) => {
  const { data } = event;
  const transformedData = doSomeHeavyWork(data);

  postMessage(transformedData);
}

Event handler onmessage runs a function every time it receives a message, with access to Event object, which holds the sent message in the data attribute.

To receive the message from the worker, we also declare onmessage event handler on the main page:

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

myWorker.postMessage('Giggity');

myWorker.onmessage((event) => {
  const { data } = event;
  doSomethingWihtIt(data);
})

And in any case you need to terminate the worker from the main thread, it can be done immediately calling terminate method:

myWorker.terminate()

Conclusion

That was a basic intro to the Web Workers. You can start using them in your application to enhance user experience.
Any intense computation can be set to run on workers, like processing large data sets, prefetching and cashing, multimedia analyze or real-time text formatting.

Oldest comments (2)

Collapse
 
riteshmukim profile image
Ritesh Mukim

It's a good article for getting started. Thanks :)

Collapse
 
thecodingalpaca profile image
Carlos Trapet

Nice post, but I don't think I can see the advantages of using worker threads in the frontend where most of the interaction is single-user and quasi "linear".

Can you think of any scenario where this would be useful?
Maybe an event-logger that does heavy processing and is just a fire-and-forget...