Web Workers provide a way to run JavaScript code in the background, allowing for parallel execution inside the browser. This is useful in scenarios where synchronous JavaScript execution may cause performance issues. In this article, we will explore the use of Web Workers, how to communicate with them, their lifecycle, how to load libraries, and the available APIs.
Introduction
JavaScript is single-threaded, meaning that only one task can be executed at a time. While this has its advantages in terms of simplicity, it can also limit the performance of JavaScript applications. Web Workers address this limitation by introducing the possibility of parallel execution.
However, Web Workers come with a few limitations. They have no access to the DOM (Window and Document objects), can only communicate with the main JavaScript program through messaging, need to be loaded from the same origin, and do not work when served using the file protocol.
Browser Support for Web Workers
Web Workers have good browser support. You can check for Web Workers support using the following code snippet:
if (typeof Worker !== 'undefined') {
// Web Workers are supported
}
Create a Web Worker
To create a Web Worker, you need to initialize a Worker object and load a JavaScript file from the same origin.
const worker = new Worker('worker.js');
Communication with a Web Worker
There are two main ways to communicate with a Web Worker:
- Using the
postMessage
API offered by the Worker object. - Using the Channel Messaging API.
Using postMessage
in the Web Worker object
You can send messages to a Web Worker using the postMessage
method on the Worker object. Here’s an example:
// main.js
const worker = new Worker('worker.js');
worker.postMessage('hello');
// worker.js
onmessage = event => {
console.log(event.data);
}
onerror = event => {
console.error(event.message);
}
A worker can also send messages back to the main thread using its global postMessage()
function:
// main.js
const worker = new Worker('worker.js');
worker.postMessage('hello');
worker.onmessage = event => {
console.log(event.data);
}
If you want to set up multiple listeners for the message
event, you can use addEventListener
instead of onmessage
:
// worker.js
addEventListener('message', event => {
console.log(event.data);
postMessage('hey');
}, false);
addEventListener('message', event => {
console.log(`I'm curious and I'm listening too`);
}, false);
addEventListener('error', event => {
console.log(event.message);
}, false);
Using the Channel Messaging API
Instead of using the built-in postMessage
API, you can use the more general-purpose Channel Messaging API to communicate with Web Workers.
// main.js
const worker = new Worker('worker.js');
const messageChannel = new MessageChannel();
messageChannel.port1.addEventListener('message', event => {
console.log(event.data);
});
worker.postMessage(data, [messageChannel.port2]);
// worker.js
addEventListener('message', event => {
console.log(event.data);
});
A Web Worker can send messages back by posting a message to messageChannel.port2
:
addEventListener('message', event => {
event.ports[0].postMessage(data);
});
Web Worker Lifecycle
Web Workers are launched and will be shut down as soon as their code is run through completion, unless they stay in listening mode for messages through worker.onmessage
or by adding an event listener. You can stop a Web Worker using its terminate()
method from the main thread or close()
method from within the worker itself.
// main.js
const worker = new Worker('worker.js');
worker.postMessage('hello');
worker.terminate();
// worker.js
worker.onmessage = event => {
console.log(event.data);
close();
}
worker.onerror = event => {
console.error(event.message);
}
Loading Libraries in a Web Worker
Web Workers can use the importScripts()
global function to load libraries in their global scope.
importScripts('../utils/file.js', './something.js');
APIs Available in Web Workers
While Web Workers do not have access to the DOM, they can use many other APIs, including:
- XHR API
- Fetch API
- BroadcastChannel API
- FileReader API
- IndexedDB
- Notifications API
- Promises
- Service Workers
- Channel Messaging API
- Cache API
- Console API (console.log() and friends)
- JavaScript Timers (setTimeout, setInterval, etc.)
- CustomEvents API (addEventListener() and removeEventListener())
- Current URL (accessible through the
location
property in read mode) - WebSockets
- WebGL
- SVG Animations