Working with Shared Web Workers

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:

javascript
	// 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:

javascript
	// 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 using self.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 new SharedWorker instance and use the port property to interact with the worker. Communication is established by calling port.start(), and messages are sent and received using postMessage and onmessage.

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):

javascript
	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):

javascript
	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):

javascript
	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):

javascript
	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.