An Introduction to JavaScript Web Workers

Have you ever needed to run code in the browser that took so long to run your app that becomes unresponsive for a while? With HTML5 web workers, you’ll never have to experience that again.

Web workers allow you to separate long-running code and run it independently of other code running on the page. This keeps your user interface responsive even during complex operations.


What are Web Workers?

Traditionally, JavaScript is a single-threaded language. This means that nothing else can run while a piece of code is running. For example, if you have code trying to animate a DOM element, the code trying to change a variable must wait for the animation to complete before it can run.

Web workers are JavaScript files that run in a separate thread without direct access to the DOM.

One way to think of web workers is that they are pieces of code that take a long time to execute, so you give them to the browser to run in the background. Since this code now runs in the background, it does not affect the JavaScript responsible for your web page.


As a side effect, it can no longer directly interact with the rest of your code, so web workers don’t have access to the DOM. However, many other browser APIs are still available, including the WebSocket and Fetch APIs.

Web workers are not entirely isolated from the main thread, however. When a worker needs to communicate with the main thread, it can send a message, and the main thread can send its own message in response.

Why Web Workers?

Before web workers, the only time-consuming way to execute JavaScript in the browser was either:

  • Accept that the page is unresponsive for a while.
  • Break this code into asynchronous chunks.

Since an unresponsive page is usually a bad user experience, you can opt for the asynchronous option. Writing code this way means breaking it up into smaller pieces that the browser can run while it doesn’t handle the UI. Elements should be small enough that if the UI needs to be updated, the browser can finish executing the current element and take care of the UI.


Webworkers have been added to HTML5 to provide a better solution to this problem. Instead of forcing you to get creative with asynchronous code, they allow you to cleanly separate a function to run in its own isolated thread.

This made it easier for developers to write such code and also improved the user experience.

Use Cases for Web Workers

Any application that requires a lot of client-side calculations could benefit from web workers.

For example, suppose your application wants to generate a usage report and stores all customer data for privacy.

To generate this report, your web application must retrieve the data, run calculations on it, organize the results, and present them to the user.

If you tried to do this in the main thread, the user would be completely unable to use the app while the app was processing data. Instead, you can move some or all of this code into a Web Worker. This allows the user to continue using the application while calculations are being performed.

How to use web workers in JavaScript

The Web Worker API defines how to use Web Workers. Using this API involves creating a Worker object with the Worker constructor like this:

let newWorker = Worker('worker.js');

The Worker The constructor accepts the name of a JavaScript file as a parameter and executes the file in a new thread. It returns a Worker object to allow the main thread to interact with the worker thread.

Workers interact with the main thread by sending messages back and forth. You use the postMessage function to send events between the worker and the main thread. Use the on message event listener to listen to messages from the other party.

Here is a sample code. First, a main thread might look like this:

let worker = new Worker('worker.js')
worker.postMessage('Hey!')

worker.onmessage = function(e) {
console.log('Worker thread says', e.data)
}

This main thread creates a worker object from worker.js and then sends it a message with worker.postMessage. It then defines an event listener, similar in concept to a DOM event listener. An event fires each time the worker sends a message back to the main thread, and the handler logs the worker’s message to the console.

The code inside the worker (worker.js) has a task:

onmessage = function(e) {
let message = e.data;
console.log('Main thread said', message);
postMessage('Hi!')
}

It listens for all messages sent from the main thread, logs the message to the console, and returns a message back to the main thread.

The messages in this example have all been strings, but that’s not a requirement: you can send almost any type of data as a message.

The kind of workers you have seen so far are called dedicated workers. You can only access them from the file in which you created them (they are dedicated there). Shared workers are the opposite: they can receive and send messages from multiple files. Shared workers are conceptually the same as dedicated workers, but you need to use them a little differently.

Let’s take an example. Instead of using the Worker constructor, each file that wants to use a shared worker must create a worker object using Shareworker():

let sharedWorker = new SharedWorker('worker.js')

The differences don’t end there, however. For a file to send or receive a message from a shared worker, it must do so by accessing a Port object, instead of doing it directly. Here’s what it looks like:

sharedWorker.port.postMessage('Hi there!')

sharedWorker.port.onMessage = function(e) {
console.log('The shared worker sent', e.data);
}

You also need to use the port object inside the worker:

onconnect = function(e) {
const port = e.ports[0];

port.onmessage = function(e) {
console.log('Message recieved', e.data)
port.postMessage('Hello!');
}
}

The onconnect listener fires whenever a connection to a port occurs (when a on message event listener is configured in the main thread).

When this happens, the code gets the port it was just connected to from the connect event and stores it in a variable. Then the code saves the on message listener on the port object. The code then logs the message to the console and uses the port to send a message back to the main thread.

Web workers improve user experience

Web Workers are JavaScript threads that let you run complex, long-running pieces of code in the background. This code will then avoid blocking the user interface. Using web workers makes it much easier to write this type of code and improves the user experience of the application. You can create Web Workers and interact with them using the Web Worker API.


Comments are closed.