Working with Shared Web Workers
Shared Web Workers are a more advanced type of Web Workers that allow multiple scripts, even those running in different windows, tabs, or iframes, to communicate with a single worker. This can be particularly useful for tasks that need to be coordinated across different parts of an application, such as managing a shared database connection or maintaining a single WebSocket connection.
1. Creating a Shared Worker
Creating a Shared Worker is similar to creating a Dedicated Worker, but with a few key differences. Let’s start by creating a simple Shared Worker:
Step 1: Create a Shared Worker Script
First, create a new JavaScript file for your shared worker, for example, sharedWorker.js
:
// sharedWorker.js
// This array will hold all the ports connected to this worker
const connections = [];
// Listen for connection requests from the main thread
self.onconnect = function (e) {
const port = e.ports[0];
connections.push(port);
// Listen for messages from the connected port
port.onmessage = function (event) {
const message = event.data;
// Broadcast the message to all connected ports
connections.forEach((connPort) => {
connPort.postMessage(`Message received: ${message}`);
});
};
};
Step 2: Create the Main Script
Now, create the main script that will interact with the Shared Worker:
// main.js
// Create a new Shared Worker instance
const mySharedWorker = new SharedWorker('sharedWorker.js');
// Access the communication port
const port = mySharedWorker.port;
// Start the communication with the worker
port.start();
// Send a message to the worker
port.postMessage('Hello from main script!');
// Listen for messages from the worker
port.onmessage = function (e) {
console.log('Received from Shared Worker:', e.data);
};
Explanation:
The
sharedWorker.js
file contains the code that runs in the Shared Worker. It listens for connection requests usingself.onconnect
and keeps track of all connected ports. When a message is received from any port, it broadcasts that message to all connected ports.In the
main.js
file, we create a newSharedWorker
instance and use theport
property to interact with the worker. Communication is established by callingport.start()
, and messages are sent and received usingpostMessage
andonmessage
.
2. Communicating with a Shared Worker
The communication with a Shared Worker differs slightly from Dedicated Workers due to the use of ports. Each script that connects to the Shared Worker gets its own MessagePort
object, which is used for sending and receiving messages.
Example: Multiple Scripts Using the Same Shared Worker
Let’s say you have two different scripts that both use the same Shared Worker:
Script 1 (script1.js
):
const worker = new SharedWorker('sharedWorker.js');
const port = worker.port;
port.start();
port.postMessage('Hello from script 1');
port.onmessage = function (e) {
console.log('Script 1 received:', e.data);
};
Script 2 (script2.js
):
const worker = new SharedWorker('sharedWorker.js');
const port = worker.port;
port.start();
port.postMessage('Hello from script 2');
port.onmessage = function (e) {
console.log('Script 2 received:', e.data);
};
In this setup, both scripts will send messages to the same Shared Worker, and the worker will broadcast the messages to all connected scripts.
3. Use Cases for Shared Workers
Shared Workers are particularly useful in scenarios where multiple scripts need to coordinate or share resources. Some common use cases include:
Maintaining a Single WebSocket Connection: Instead of opening a new WebSocket connection in each script, a Shared Worker can manage a single connection and distribute the messages to all connected scripts.
Shared State Management: When multiple tabs or iframes need access to shared state (e.g., user preferences or authentication status), a Shared Worker can manage this state and synchronize it across all connected scripts.
Cross-Tab Communication: Shared Workers can facilitate communication between different tabs of the same application, allowing them to share data or synchronize actions.
4. Example: Using a Shared Worker Across Multiple Pages
Let’s create an example where a Shared Worker is used to synchronize a counter value across multiple pages or tabs.
Shared Worker Script (sharedWorker.js
):
let counter = 0;
const connections = [];
self.onconnect = function (e) {
const port = e.ports[0];
connections.push(port);
// Send the current counter value to the new connection
port.postMessage(`Counter value: ${counter}`);
port.onmessage = function (event) {
if (event.data === 'increment') {
counter++;
} else if (event.data === 'decrement') {
counter--;
}
// Broadcast the updated counter value to all connections
connections.forEach((connPort) => {
connPort.postMessage(`Counter value: ${counter}`);
});
};
};
Main Script (page1.js
and page2.js
):
const worker = new SharedWorker('sharedWorker.js');
const port = worker.port;
port.start();
port.onmessage = function (e) {
console.log('Received from Shared Worker:', e.data);
};
// Example: Increment the counter
document.getElementById('incrementButton').addEventListener('click', function () {
port.postMessage('increment');
});
// Example: Decrement the counter
document.getElementById('decrementButton').addEventListener('click', function () {
port.postMessage('decrement');
});
In this example, two different pages or tabs can interact with the same Shared Worker to increment or decrement a counter. The current value of the counter is synchronized across all connected pages.