Shared workers are special web workers that can be accessed by multiple browser contexts like browser tabs, windows, iframes, or other workers, etc.
They’re different from dedicated workers in that they are instances of SharedWorkers
and have a different global scope.
All browser contexts must be within the same domain in accordance with the same-origin policy.
In this article, we’ll look at the characteristics of shared and workers and how to create one.
Characteristics of Shared Workers
The difference between dedicated and shared workers is that dedicated workers can only be accessed by one script. Shared workers can be accessed by multiple scripts even if each page run inside different windows.
This enables more flexible communication between multiple scripts.
The scripts that access the workers can do so by accessing it through the MessagePort
object created using the SharedWorker.port
property. If the onmessage
event is attached using addEventListener
, then the port is manually started using the start
method.
When the port is started multiple scripts can post messages to the worker and handle messages sent using the port.postMessage
and port.onmessage
respectively.
Other than that, scripts still communicate with the shared worker by communication messages to the worker and get messages back from the worker via scripts.
The SharedWorker
constructor also takes an option object with the following options:
type
: a string specifying the type of worker to create. The value can beclassic
ormodule
and defaults toclassic
.credentials
: a string specifying the type of credentials to use for the worker. The value can beomit
(no credentials required),same-origin
orinclude
. If it’s not specified, thetype
isclass
thenomit
is the default.name
: a string specifying the identifying name for theSharedWorkerGlobalScope
representing the scope of the worker. It’s mainly used for debugging.
The constructor will throw a SecurityError
if it’s not allowed to start workers, like if the URL is invalid or same-origin policy is violated.
NetworkError
is raised if the MIME type of the worker script is incorrect.
SyntaxError
is raised is the worker’s URL can’t be parsed.
Creating and Using a Shared Worker
We can create a shared worker and use it without too much hassle. First, we have to create a shared worker. Then we have to create the scripts to use it. Finally, we add the HTML so that we can do something with it.
Start by creating a scripts
folder and add a sharedWorker.js
file:
onconnect = (ev) => {
const [port] = ev.ports;
port.onmessage = e => {
const [first, second] = e.data;
let sum = +first + +second;
if (isNaN(sum)) {
port.postMessage("Both inputs should be numbers");
}
else {
const workerResult = `Result: ${sum} `;
port.postMessage(workerResult);
}
};
};
In the code above, we have an onconnect
handler assigned to the onconnect
property. Then inside the handler function, we get the port that the shared worker uses to communicate with other scripts.
Next, we assign an event handler to the onmesaage
property of the port
that we got from the parameter of the onconnect
handler. Then we can get the data from the parameter of the onmessage
handler to and compute the sum.
Inside the handler, we check if both pieces of data sent from the external scripts are numbers and then add the numbers together if they’re and send them to the scripts.
We have to call port.start();
inside the onconnect
event handler at the end if we want to use addEventListener
to add a listener to the message
event instead of assigning an event handler to onmessage
.
Other we send a message back to the external scripts indicating that one or not pieces of data sent aren’t a number.
Then we create a main.js
and main2.js
in the script.js
and add:
const sharedWorker = new SharedWorker("scripts/sharedWorker.js");
const first = document.getElementById("number1");
const second = document.getElementById("number2");
const result = document.getElementById("result");
sharedWorker.port.start();
first.onkeyup = () => {
sharedWorker.port.postMessage([first.value, second.value]);
};
second.onkeyup = () => {
sharedWorker.port.postMessage([first.value, second.value]);
};
sharedWorker.port.onmessage = e => {
result.textContent = e.data;
};
We get the input from the HTML file which we’ll create and then send it to our shared worker. We have to use the port
of the shared worker to do this as we do by using sharedWorker.port.postMessage
.
In:
sharedWorker.port.onmessage = e => {
result.textContent = e.data;
};
We get back the data sent from the shared worker.
Both main.js
and main2.js
do the same thing but they’re used by different pages.
Finally, create index.html
and index2.html
and add:
<!DOCTYPE html>
<html>
<head>
<title>Add Worker</title>
</head>
<body>
<form>
<div>
<label for="number1">First Number</label>
<input type="text" id="number1" value="0" />
</div>
<div>
<label for="number2">Second Number</label>
<input type="text" id="number2" value="0" />
</div>
</form>
<p id="result">Result</p>
<script src="scripts/main.js"></script>
</body>
</html>
In the end, we get:
When we go to index.html
or index2.html
. We should be able to do calculations on both pages without them interfering with each other.
Creating a shared worker isn’t too different from creating and dedicated worker. The constructor arguments are exactly the same. The only difference is that we can use it with more than one script and we need to get the port
object to communicate between external scripts and shared workers.