Test If the Feature Exists
Unless you know for certain that your users will be using a specific browser version, there is a chance that your website will be accessed by a browser that does not have the HTML5 feature you wish to use.
It is always best to test if the browser supports the feature you intend to use before you try using the feature.
Both dedicated and shared workers will exist as objects on the global window object if the browser supports them.
The following is an example of how you can test to see if the browser supports dedicated or shared workers:
if (window.Worker) { /* Dedicated Workers are supported */ }
if (window.SharedWorker) { /* Shared Workers are supported */ }
To create a dedicated worker, you simply create a new instance of the Worker object passing in a string that specifies the location of the JavaScript file that contains the worker thread's main code.
The following is an example of how a dedicated worker can be created:
var aDedicatedWorker = new Worker("DedicatedWorker.js");
A shared worker is created the same way you create a dedicated worker with the only change being that, rather than specifying a Worker object, we specify a SharedWorker object:
var aSharedWorker = new SharedWorker("SharedWorker.js");
The worker objects were designed to accept a string indicating the location of the JavaScript file (rather than a function) in order to prevent the creation of a closure that would allow the worker to gain direct access to the browser's DOM or other page elements.
Receiving Messages from a Worker
Communication between workers and the thread that created them is accomplished by passing messages.
Since several HTML5 technologies pass messages, a separate specification exists that is specifically for messages called HTML5 Web Messaging. I mention this because certain details critical to how Web Workers work are not found within the web worker specifications but rather within the web messaging specifications.
In order to receive messages from a worker, you need to attach to the onmessage event handler of the worker instance.
The following is an example of how you can set up an event listener for a dedicated worker:
aDedicatedWorker.onmessage = OnWorkerMessage;
Setting up the event listener for a shared worker object is similar to the dedicated worker but in this case you need to access the port object as in the following example:
aSharedWorker.port.onmessage = OnWorkerMessage;
For both dedicated and shared workers, you can also attach to the message event handler event type by using the addEventListener method as in the following example:
aSharedWorker.port.addEventListener("message", OnWorkerMessage, false);
One thing to be aware of if you use the addEventListener method is that, for shared workers, you will also need to start the worker:
aSharedWorker.port.start();
The onmessage event listener method will be passed by a MessageEvent parameter which will have a .data property containing the message passed from the worker thread.
The following is our onmessage event listener (there is no difference between dedicated and shared workers here):
function OnWorkerMessage(evt){
alert("Message received from the worker: " + evt.data);
}
Passing Messages to a Worker
To pass a message to a dedicated worker, you call the postMessage function of the worker instance as in the following example:
aDedicatedWorker.postMessage("Hello from the UI thread!");
Passing a message to a shared worker is similar but in this case the postMessage function is found on the port object:
aSharedWorker.port.postMessage("Hello from the UI thread!");
The postMessage function is not limited to just passing strings between threads.
You can also pass structured data like JSON objects, JavaScript objects like numbers and Dates, as well as certain data objects like File Blob, FileList and ArrayBuffer.
The following is an example of passing an object:
var objData = {
"employeeId": 103,
"name": "Sam Smith",
"dateHired": new Date(2006, 11, 15)
};
aDedicatedWorker.postMessage(objData);
Because the objects are passed between the two threads so seamlessly (we pass the object in from one side and receive it on the other side without any special work on our part), it feels like the data is passed directly but by default that is not actually the case. By default, all data is actually copied between the threads (also referred to as structured cloning).
When you pass data to another thread, behind the scenes the object is serialized into a string, passed to the other thread, and then de-serialized back into an object on the other side.
For the most part you probably won't notice too much of a performance hit if you're just passing small amounts of data, but if you need to pass large amounts of data, this cloning may result in noticeable delays.
Fortunately, some browsers now support a concept called transferable objects where the ownership of the data is transferred to the other thread. The data is moved rather than copied, which can result in large performance improvements.
When you transfer an object like an ArrayBuffer to another thread, for example, the contents are transferred to the other thread leaving an empty ArrayBuffer object in the calling thread. To transfer an object to a thread you still use the postMessage method but also include an array in the optional second parameter indicating which items in the message should be transferred.
At the time of this writing, not all browsers support the second parameter of the postMessage method and will actually throw an exception if you attempt to use the second parameter. It is recommended that you use a try/catch statement around your postMessage call if you wish to use this technique.
If you're passing in just the one object, as in the following example, then you can specify the object itself in the array:
var abBuffer = new ArrayBuffer(32);
aDedicatedWorker.postMessage(abBuffer, [abBuffer]);
Not all objects are transferable but, if the object you're passing contains a transferable object, you can specify just the portion of the object that you want to be transferred in the transfer parameter array as in the following example:
var objData = {
"employeeId": 103,
"name": "Sam Smith",
"dateHired": new Date(2006, 11, 15),
"abBuffer": new ArrayBuffer(32)
};
aDedicatedWorker.postMessage(objData, [objData.abBuffer]);
If the object you are passing to the other thread contains multiple transferable objects, you can specify each item by comma separating them in the transfer parameter array.
Not all browsers support transferable objects. The following example demonstrates a way to detect if a browser supports transferable objects by actually trying to pass a small amount of data in an ArrayBuffer to another thread. If the buffer's length is zero after the postMessage call, then we know that the data was transferred rather than simply copied:
var abBuffer = new ArrayBuffer(32);
aDedicatedWorker.postMessage(abBuffer, [abBuffer]);
if (abBuffer.byteLength) {
alert("Transferrable Objects are not supported by your browser");
}
Error Handling
For both dedicated and shared workers you can attach to the onerror event handler of a worker instance so that you can be informed if a runtime error occurs (e.g., maybe there is a network error preventing the creation of the worker object).
For dedicated workers, the onerror event handler will also tell you about any unhandled exceptions from within the worker thread.
The following is an example of how you would add an event listener for a onerror event on a worker object:
aDedicatedWorker.onerror = function(evt) { alert(evt.message); }
aSharedWorker.onerror = function(evt) { alert(evt.message); }
ErrorEvent properties |
Description |
message |
A human-readable error message |
filename |
The name of the script file in which the error occurred (in some browsers this property is undefined and in other browsers the property is an empty string) |
lineno |
The line number of the script file on which the error occurred (in some browsers this is set to zero) |
Closing a Worker
Both a dedicated and shared worker thread can be closed by calling terminate on the worker instance as in the following example:
aDedicatedWorker.terminate();
aSharedWorker.terminate();
One thing to be aware of with calling terminate is that it does not give the worker thread an opportunity to finish its operations or to clean up after itself.
The following is a summary of the JavaScript that creates a dedicated and shared worker:
var gDedicatedWorker = null;
var gSharedWorker = null;
$(document).ready(function () {
// If Dedicated Workers are supported by this browser...
if (window.Worker) {
gDedicatedWorker = new Worker("DedicatedWorker.js");
gDedicatedWorker.onerror = handleError;
gDedicatedWorker.onmessage = handleMessage;
gDedicatedWorker.postMessage("Hello from the UI thread!");
} // End if (window.Worker)
// If shared Workers are supported by this browser...
if (window.SharedWorker) {
gSharedWorker = new SharedWorker("SharedWorker.js");
gSharedWorker.onerror = handleError;
gSharedWorker.port.onmessage = handleMessage;
gSharedWorker.port.postMessage("Hello from the UI thread!");
} // End if (window.SharedWorker)
});
// OnError event listener
function handleError(evtError) {
if (typeof evtError === "string") {
updateResults("Error: " + evtError);
}
else if (evtError.message) {
updateResults("Error...Message: " + evtError.message +
", File name: " + evtError.filename +
", Line number: " + evtError.lineno);
}
else {
updateResults("Unknown error");
}
}
// OnMessage event listener
function handleMessage(evtMessage) {
updateResults("Message received from the worker: " + evtMessage.data);
}
// A helper to update a div on the page with a new message
function updateResults(sMessage) {
var $divResults = $("#divResults");
$divResults.html(($divResults.html() + "" + sMessage));
}
{{ parent.title || parent.header.title}}
{{ parent.tldr }}
{{ parent.linkDescription }}
{{ parent.urlSource.name }}