[whatwg] Combining the DedicatedWorker and SharedWorker interfaces

Hello all,

A few months ago, I suggested that we combine the DedicatedWorker and
SharedWorker interfaces
(http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2008-September/016228.html),
but we never reached consensus.

Since Mozilla and Apple are both working on implementing this now, I'd
like to revisit the issue before we end up with something non-ideal
baked into implementations.

My biggest issue with the proposal as currently drafted is that there
are so many different ways to send and receive messages. I think this
overcomplicates the proposal for both developers and implementors.

For dedicated workers, you can either send single messages using the
Worker object directly, like in Gears:

var worker = new Worker("foo.js");
worker.postMessage("ping");

Or, you can use the more powerful startConversation() API to easily
group related messages together:

var port = worker.startConversation();
port.postMessage("ping");

Note that the worker has to know ahead of time which API the callers
will use since the way that it replies is different depending on that.
If the caller used Worker.sendMessage(), the worker should reply like
this:

onmessage = function() {
  postMessage("pong");
}

... but if the caller used Worker.startConversation().sendMessage(),
then the worker should reply like this:

onmessage = function(e) {
  e.port.postMesage("pong");
}

SharedWorkers require a third, completely different API to send messages:

var w = new SharedWorker("foo.js", "foo");
w.port.postMessage("ping");
w.port.onmessage = function(e) {};

The interface to receive messages in a SharedWorker is also special:

onconnect = function(e) {
  e.port.onmessage = function(e) {
    e.port.postMessage("pong");
  }
}

This lack of generality bothers me on an aesthetic level, but I also
think it has the following real problems:

* Workers have to know what interface was used to send them messages.
If the page using a worker decide to start using a more powerful send
API, the worker must also be upgraded. You can already see examples of
this problem in the samples at the beginning of the draft. They are
marked with the comments "// support being used as a shared worker as
well as a dedicated worker".

* Having different interfaces for each use case means that each new
feature has to be added to each interface separately. We can already
see this problem in the fact that the SharedWorker interface lacks
startConversation() for no apparent reason.

* Having multiple interfaces probably increases the chance of
developers misunderstanding and using the wrong tool for the job. I
can easily see developers accidentally reimplementing
startConversation() on top of Worker.sendMessage().

* More API for developers to learn and implementors to build.


I think that these issues can all be addressed by simplifying and
combining the various APIs. This will make the simplest examples of
workers require slightly more code, but I think it is much simpler and
more elegant.

Here is how it would work:
* Get rid of the DedicatedWorker interface.
* Add startConversation() to SharedWorker, but rename it "connect()"
and make the onconnect event fire inside the worker each time it is
called.

Here's an example in code:

// dedicated workers (outside)
var worker = new Worker("foo.js");
var port = worker.connect();
port.onmessage = function() { }
port.postMessage("ping");

// dedicated workers (inside)
onconnect = function(e) {
  e.port.onmessage = function(e) {
    e.port.postMessage("pong");
  }
}

Shared workers are exactly the same except the constructor is
SharedWorker("foo.js", "foo");

Note that I do not think it is necessary to implement this all at
once. For one, the SharedWorker constructor could easily be punted for
future releases.

Thoughts?

- a

Received on Monday, 3 November 2008 23:08:58 UTC