I\'m trying to use a shared worker to maintain a list of all the windows/tabs of a web application. Therefore following code is used:
//lives in shared-worker.js
I have been neck deep in the documentation all week working around the same problem.
The problem is the MessagePort specification. The bad news being that it has no error handling, and no flag, method or event to determine whether it has been closed.
The good news is I have created a viable solution, but it's a lot of code.
Keep in mind even among the supporting browsers the activity is handled differently. For example Opera will throw an error if you attempt to message or close a closed port. The bad news is you have to use a try-catch to handle the error, the good news is you can use that feedback to close a port on at least one-side.
Chrome and Safari fail silently leaving you no feedback and no way to end invalid objects.
My solution involves delivery confirmation or a custom "callback" approach. You use a setTimeout and pass the ID for it to the SharedWorker with your command, and before processing the command it sends back a confirmation to cancel the timeout. That timeout is generally hooked to a closeConnection() method.
This takes a reactive approach instead of a pre-emptive, originally I toyed with using the TCP/IP protocol model but that involved creating more functions to handle each process.
Some Psuedo-Code as an example:
function customClose() {
try {
worker.port.close();
} catch (err) { /* For Opera */ }
}
function send() {
try {
worker.port.postMessage({command: "doSomething", content: "some Data", id: setTimeout(function() { customClose(); ); }, 1000);
} catch (err) { /* For Opera */ }
}
function respond(p, d) {
p.postMessage({ command: "confirmation", id: d.id });
}
function message(e) {// Attached to all ports onmessage
if (e.data.id) respond(this, e.data);
if (e.data.command) e.data.command(p, e.data);// Execute command if it exists passing context and content
}
I have placed a complete demonstration here: http://www.cdelorme.com/SharedWorker/
I am new to stack overflow, so I am not familiar with how they handle large code posts, but my full solution is two 150 line files.
Just using delivery confirmation alone is not perfect, so I have worked at improving it by adding additional components.
In particular I was investigating this for a ChatBox system, so I wanted to use EventSource (SSE), XHR, and WebSockets, only XHR is supported inside SharedWorker objects supposedly, which creates a limitation if I wanted to have the SharedWorker do all the server communication.
Plus since it needs to work for browsers without SharedWorker support I would be creating long-hand duplicate processing inside the SharedWorker which doesn't make a lot of sense.
So in the end if I implement SharedWorker it would be as a communication channel for the open tabs only, and one tab will be the Control Tab.
If the control tab is closed, the SharedWorker won't know, so I added a setInterval to the SharedWorker to send an empty response request every few seconds to all open ports. This allows Chrome and Safari to eliminate closed connections when no messages are being processed, and allows the control tab to change.
However, this also means if the SharedWorker process dies the tabs must have an interval to check in with the SharedWorker using the same approach every so often, allowing them to use the fallback approach of every-tab-for-themeselves that is inherent to all other browsers using the same code.
So, as you can see a combination of callbacks for delivery confirmation, setTimeout and setInterval must be used from both ends to maintain knowledge of connectivity. It can be done but it's a giant pain in the rear.