W3C home > Mailing lists > Public > public-webtiming@w3.org > June 2015

Re: Specifying the association with an online timing service

From: Ingar Mæhlum Arntzen <ingar.arntzen@gmail.com>
Date: Tue, 9 Jun 2015 16:35:36 +0200
Message-ID: <CAOFBLLrEACs2ubrMW5N_FT0gZ3qtz47hN0qRpjfUZP=Nmg-n0Q@mail.gmail.com>
To: Francois Daoust <fd@w3.org>
Cc: public-webtiming@w3.org
Hi Francois and all.

Your post on association between timing objects and timing providers opens
the floor for several discussions (by intention I suspect :) ).

I'll try to highlight one issue here, and leave others for later.

>From the programmer perspective, it is quite tedious to have to wait for an
object to reach some state of readiness, before you can start to use it.

In this perspective, it would be nice if timing objects may be
intantiated/generated up front as "empty shells", yet allowing application
handlers to be safely registered immediately. As bootstrapping is
finalized, the "empty shells" are filled up, and events are being emitted
as expected - causing application code to start executing.

The requirement to provide this guarantee would have to be reflected in the
spec somehow. I'm not sure how though - maybe there is some precedence in
other event interfaces?

Related to this, there is one more detail for the event interface that Njål
and I would like to propose.

Quite often in Web interfaces, you need to check whether an event has
already been emitted or not. For instance, consider the "onopen" event. To
be sure of correctness you often end up with code like this

if (is_open()) {
  do_your_thing ();
} else {
  onopen = function () {do_your_thing();}
}

Francois, you have a very similar example in your example code in this
thread.

Now, this may not look like much of an inconvenience, but it adds up. This
may have to be done for multiple event types, and again recursively in
higher-level api's that depend on underlying events.
In my experience, it quickly gets difficult to reason about correctness as
well as tracking down bugs. These scenarios may even lead to nasty effects
such as event duplication or reordering.

This problem also generalizes to "observing" or visualizing any object that
has state and may be subject to modification.

// visualize the state of an object
var state = o.get_state();
visualize(state);
// attach handler for future state changes
o.on("change", function () {
   var state = o.get_state();
   visualize(state);
})

We have successfully avoided this complexity (for the event consumer) by a
subtle change in the eventing api. Basically we are shifting the
responsibility for sorting this out from the event consumer (examples
above) to the event source. So, when an event consumer attaches a handler,
the event source is responsible for sending an initial event with the
current state, before any subsequent event.

This makes life very simple for the consumer/programmer. Simple statements
like
onopen = do_your_thing;
or
o.on("change", function () {
   var state = o.get_state();
   visualize(state);
})

are sufficient. There is less code duplication and if/else constructs.
Also, the mental model is somewhat simplified. You can always trust the
event source to give you the whole story.

I suppose this pattern has a name but I'm not aware of it - sorry :(

Finally, this idea ties in neatly with the bootstrapping issues discussed
above.

timing_object.on("change", function () {//react})

will be an entry point for all timing sensitive app code, and it will
always work, independent of the state of the timing_object at the time when
the app handler is registered.

Again, I'm not sure how to include this in the spec. Ideally, if this
pattern was documented in some specific EventListenerInterface we could
simply reference that - but I suspect it is not?

Also, to explain the (slightly more complex) logic of the event source, we
could provide some sample code if needed.


Best,

Ingar



















2015-06-08 17:18 GMT+02:00 Francois Daoust <fd@w3.org>:

> Hi again,
>
> Looking at the TimingObject interface as defined in the spec [1], I'm
> wondering what would be the proper way to associate a TimingObject instance
> with an online timing service and to start using it.
>
> The spec currently currently defines an under-specified "src" attribute
> but I'm wondering whether it's of any use in the end: using a URL would
> probably require that we define a custom URL scheme to tell the user agent
> about the protocol to use. Since we want to leave some freedom for online
> timing services to define their own mechanisms, that does not seem to be a
> good approach and I would just drop it for now.
>
> I explored the possibility to use an "srcObject" attribute to associate a
> local instance with a TimingProvider instance connected to an online timing
> service in my prototype [2]. However, this means that, before being able to
> use the timing object, the code needs to listen to the "readystatechange"
> event, which is somewhat tedious.
>
> Also, "srcObject" could be re-assigned at any time, so the code needs to
> take that possibility into account.
>
> var runWhenReady = function () { [[...]] };
> var provider = [[get timing provider instance]];
>
> var alreadyRun = false;
> var timing = new TimingObject();
> timing.srcObject = provider;
> timing.onreadystatechange = function (evt) {
>   if (!alreadyRun && (evt.value === 'open')) {
>     alreadyRun = true;
>     run();
>   }
> };
>
> To simplify the life of developers, it might be better to define a
> dedicated "open" event (as in the WebSockets API) instead of a
> "readystatechange" event and, assuming the TimingProvider has a simple
> (connecting, open, closing, closed) lifecycle, to prevent the timing object
> from being associated with more than one TimingProvider, e.g. by dropping
> the srcObject attribute and rather passing the TimingProvider instance as
> part of the constructor, as in:
>
> var timing = new TimingObject(provider);
> timing.onopen = runWhenReady;
>
> Perhaps more fashionable, there could be a ready() method that returns a
> Promise that resolves when the association is ready and rejects when
> connection goes wrong:
>
> var timing = new TimingObject(provider);
> timing.ready().then(runWhenReady);
>
> ... but I think I still prefer the "onopen" mechanism. Any thought?
>
>
> The "TimingProvider" interface would also need to be specified. In the
> end, all I needed was something pretty close to the TimingObject interface:
>
> interface TimingProvider : EventTarget {
>   readonly attribute DOMString readyState;
>   readonly attribute StateVector vector;
>   readonly attribute Interval range;
>   attribute EventHandler onreadystatechange;
>   StateVector query();
>   void update(StateVector newVector);
>   void close();
> }
>
> I'm not entirely convinced that the "vector" attribute needs to be exposed
> in practice but that's a minor detail.
>
>
> The interface is so close to TimingObject that it begs the question as to
> whether a timing object associated with an online timing service should not
> be seen as a derived TimingObject class rather than as a TimingObject
> associated with a TimingProvider. The only real difference is the
> "timeupdate" event (and a few helper methods that could also be added to a
> TimingProvider). I guess it makes sense to keep a distinction if user
> agents implement the TimingObject interface natively whereas TimingProvider
> classes may be defined in regular JS libraries.
>
> Should I sketch some text along these lines for the TimingProvider
> interface?
>
> Francois.
>
>
> [1] http://webtiming.github.io/timingobject/
> [2] https://github.com/tidoust/timingservice
>
>
Received on Tuesday, 9 June 2015 14:36:12 UTC

This archive was generated by hypermail 2.4.0 : Friday, 17 January 2020 19:25:14 UTC