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

Re: Specifying the association with an online timing service

From: Njaal Borch <njaal.borch@norut.no>
Date: Wed, 10 Jun 2015 08:32:21 +0200
Message-ID: <CAOc996sbH3_oAhVhBptQxeNjBFSa_=SuEp1NUPPb_fvcXOE0vA@mail.gmail.com>
To: Ingar Mæhlum Arntzen <ingar.arntzen@gmail.com>
Cc: Francois Daoust <fd@w3.org>, public-webtiming@w3.org
Hi guys,

there's even the rather horrible race condition in all the two-step
versions:

if somestate execute func1;
                          <---  somestate changes
somestate.on("change", func1);

In this case, func1 will not be called.  Ref. Murphy and the fact that
during init, a lot of things happen quickly, this will of course happen far
more often than you expect. :-)

Switching it around gives you duplicates, which you will need to filter out:
somestate.on("change", func1)
if somestate execute func1;

I actually experience this kind of thing with the video.canplay event,
which on my typical machines on the current versions of browsers act
differently.  Chrome emits video.canplay on dektops (where it can play),
not on mobiles (which require a user event).  Firefox might emit
video.canplay, but is normally ready before I get to the point of
checking.  I therefore check if video.canplay, then create a new canplay
event and emit that, but that means often getting dupes on Chrome.  Very
annoying and unnecessarily complicated.

Of course, these things become more complicated if the emitted event does
not map directly down to a single parameter you can check.  The
timingObject returns a tuple for example, and you can't really tell if it
has been changed or not, in particular if the default values are (0,0,0)
for a timingObject with no source (i.e. a local timing object).  On the
other hand, you must be able to handle the initial event (if your code is
executed before the somestate is set), so *no complexity* is added for the
programmer if the initial event is emitted on register.

So the quick summary is:
when a handler is registered on an object, it should emit the event it
would have emitted had the handler been registered earlier.  I think this
could be safe for all non-user-initiated events.

N




---
Dr. Njål Borch
Senior researcher
Norut Tromsø, Norway

On 9 June 2015 at 16:35, Ingar Mæhlum Arntzen <ingar.arntzen@gmail.com>
wrote:

>
> 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 Wednesday, 10 June 2015 06:32:53 UTC

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