Introduction to Web Workers

Web Workers are a native browser API that have had mainstream support for several years, but their use often gets overshadowed by the Service Workers that make applications offline-capable and provide an extra layer of caching on the client side.

In my own experience, I also used Service Workers several years before delving into Web Workers. In this post, I'll go over the benefits of Web Workers and show a practical example of their use.

What Are Web Workers?

Web Workers are JavaScript files that run in a separate thread from the rest of the application. The main benefit of Web Workers is that they can perform calculations without causing the rest of the user interface to become unresponsive.

There are two essential ways that the main and worker threads communicate, postMessage to send data and addEventListener to respond to the data being sent. Communication is bidirectional – it can flow either from the worker to the main thread or vice versa.

It looks like this:

// main thread
// register a worker
const worker = new Worker('worker.js');

// send data to the worker
worker.postMessage('Hey there, worker.');

// handle the response received back from the worker
worker.addEventListener(
  'message',
  e => {
    console.log('Received message back from worker:', e.data);
  },
  false
);
// worker thread (worker.js)
self.addEventListener('message', e => {
  console.log(e.data); // Hey, there worker.
  self.postMessage('Back to you, main thread'); 
  // handled with worker.addEventListener in main thread
});

This back-and-forth communication allows certain parts of an application to be offloaded to the worker where it makes sense to do so.

What Can be Offloaded to Web Workers?

Web Workers don't have access to everything that is available to the main thread, so not everything is an eligible candidate for offload. For example, you can't use document.querySelector in Web Workers because there is no DOM and therefore no access to the document object.

Although Web Workers have a limited subset of functions, there are certain areas where they provide obvious performance gains, such as parsing and filtering large arrays, computing large amounts of data, working with IndexedDB, or even making async requests with fetch or XMLHttpRequest.

In summary, you can use the main thread exclusively for rendering information and binding events to visual elements on the page, and Web Workers can take over computation and data manipulation behind the scenes, as long as the Web Worker has access to the particular API you are trying to use.

An Example of Web Workers in Action

Part of the problem with many of the Web Worker demos, and the reason for my initial hesitation to use them, is that they either cover completely nonsensical use cases or they are so advanced they become convoluted.

I wanted to provide a more accessible use case, so I wrote a demo where a Web Worker is used to fetch data when a button is clicked on a web page. Once the data is retreived, it is then formatted in the worker file, and then the markup is passed back to the main thread so it can be inserted on the page.

You can also see the source code on Github.

Fetching the data in this way is a bit more involved than handling everything in a single thread since the communication between the main and worker threads have to be managed, but it is still reasonable. Additionally, using Web Workers can provide a clear performance boost, expecially when the computation gets complex and the data sets get bigger.

Refactoring Existing Applications

Part of the process of learning about Web Workers is finding opportunities to use them in an actual project. For me, that opportunity came when I rewrote a small JavaScript application I use for guitar practice.

Originally written in 2012, the Guitar Arpeggio Visualizer was in need of a facelift. Since it is something that I wrote for myself and still use regularly, I took the opportunity to rewrite the application entirely and used Web Workers for much of the note and scale degree calculation for the diagrams.

What I learned from that process is that nearly every web-facing JavaScript application that doesn't use Web Workers has an opportunity to use them and would likely benefit from doing so.

Conclusion

Web Workers are now supported in every major browser. They are a powerful piece of technology that requires a little more planning and effort to implement compared to an application that relies solely on a single thread, but given the performance gains and perceived speed that a user will experience due to a more responsive UI, I plan on using them much more often.