Note that there are some explanatory texts on larger screens.

plurals
  1. POTypeScript-enhanced SharedWorker PortMessage channel contracts
    primarykey
    data
    text
    <p><strong>What is a good general design, taking advantage of TypeScript language features, for an RPC-style communication channel between Windows and Workers?</strong></p> <p><strong>SharedWorkers</strong> are JavaScript code listings that are run by the browser outside the context of a specific browser window. A single SharedWorker (by script URI) is shared by all open browser windows whose own code request it. Windows and workers communicate through the entangled <code>worker.port</code> objects to which a sender can postMessage() and from which a receiver can listen for 'message' events. The most consistently supported type allowed to be posted is a single <code>string</code> which can easily be a <code>JSON.stringify(ofSomeObject)</code> but may be of any custom format.</p> <p><strong>TypeScript</strong> is an extension of JavaScript which provides some useful type safety and other helpful application code management features.</p> <p><strong>Remote Procedure Call</strong> libraries such as WCF uses SOA-style Contract interfaces shared by both (all) sides of a communication channel and abstracts the monotonous and error-prone necessity of serializing and deserializing requests.</p> <hr> <p>Communicating between browser Windows and their Worker is similar to RPC since we cannot pass code as data through the channel. One main difference is that communications are one-way only. A sender can post a message but cannot block and wait for a result. Instead, the application layer is responsible for matching received data to sent requests (if appropriate).</p> <p>To take advantage of the typing features of TypeScript, it seems to me that following the Contract paradigm is a good choice. We need:</p> <ol> <li>one interface for each receiver's service Contract.</li> <li>a receiver implementation of each Contract</li> <li>a proxy implementation of each Contract</li> </ol> <p>First we need to teach TypeScript about SharedWorkers building on lib.d.ts definitions. SharedWorker.d.ts:</p> <pre><code>interface ConnectEvent extends Event { ports: MessagePort[]; }; interface SharedWorker extends EventTarget, AbstractWorker { port: MessagePort; }; declare var SharedWorker: { prototype: SharedWorker; new (scriptURL: any, name: any): SharedWorker; new (scriptURL: any): SharedWorker; } </code></pre> <p>In WCF, we have an ambient OperationContext to know about the connected client. We also return a value in most cases to send a response to a request. </p> <p>In JavaScript, we don't have ThreadStatic and I doubt we can reliably provide an ambient equivalent of OperationContext. (Each code listing is run single-threaded, but the way asynchrony is implemented with the timer callbacks and possibly employed by the Worker implementation I don't think we could keep track of it right.)</p> <p>With MessagePorts, we can't return a value. Instead we have to send another one-way response back to the requester and so we need a handle to the return MessagePort. However, this return MessagePort cannot be part of the Contact since the Proxy implementation would not have a need for it. All I can think of to do is create a new instance of the Implementation class initialized with receiver/proxy-specific differences such as the return MessagePort and any other data, such as the array of all connected MessagePorts for broadcast messages. Then the member methods of the implementation can reference those as they might an OperationContext.</p> <p>And an example of the message event handler:</p> <pre><code>worker.port.addEventListener('message', (e: MessageEvent) =&gt; { var data = JSON.parse(e.data); new ReceiverImplementation(worker.port)[data.name].apply(this, data.params); }, false); </code></pre> <p>A proxy implementation might have a function such as:</p> <pre><code>SessionLogin(username: string) { this.worker.port.postMessage(Command('SessionLogin', arguments)); } </code></pre> <p>The message event handler applies the received parameters to the specified method name. The ReceiverImplementation object ought to be independent as shown, perhaps, from any other class since we would want to avoid method name collisions. Perhaps not though - we're talking among client code run on a client machine. There aren't the same security concerns (nor ability to enforce guards).</p> <p>Posting the whole prototype is too long/complicated and because of the subject matter, it won't fit into a jsfiddle. And anyway, what I have isn't that great. It still seems like a bunch of boilerplate code for very little functionality.</p> <p>And I'm pretty sure all of this would apply to non-Shared Web Workers as well, but with all this trouble of communicating, I don't see the value proposition for the non-shared variety!</p> <p><em>This is a sprawling question</em>, but I hope I've done an OK job of explaining it. What are your ideas for solving this Window-Worker communication? How can the relaxed nature of JavaScript and the mere illusion of safety of TypeScript be exploited for a simpler and less error-prone communication? I suspect I'm taking the .NET-limitations-inspired WCF and porting them too closely to JavaScript.</p> <p>Thanks.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload