Re: Observing state and changes (was: Re: Specifying the association with an online timing service)

Hi. Francois

I agree about the concern that this breaks convention. I don't think it
violates any code practices though. As long as the user agent triggers that
initial event by using the event queue - registering a handler will not
have any (synchronous) side effects. When you register a handler you must
always expect that it may fire an event asynchronously at any time, so
event emitted 0.1 second afterwards should not be more surprising than say
4.2 seconds afterwards.

So, implemented correctly, I don't think this qualifies as an anti-pattern.
The only thing it changes is that life becomes easier for the programmer.
Of course, if you are not aware of the semantics, you'll probably query
state first and then get that state repeated on the initial event after
registering a handler.

Ingar


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

> Hi Ingar,
>
> On 2015-06-09 16:35, Ingar Mæhlum Arntzen 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?
>>
>
> Yes. In my example:
>
>  var timing = new TimingObject(provider);
>  timing.onopen = runWhenReady;
>
> My implicit assumption is that the TimingObject constructor would always
> return an instance in the "connecting" state (is that what you call an
> "empty shell"?) and that transition to the "open" state would always happen
> in a following execution task (in a next "tick" in other words), so that
> attaching event listeners after instance creation would always work.
>
> That guarantee should indeed be written explicitly in the spec. This can
> be done using "queue a task" as defined in HTML5:
> http://www.w3.org/html/wg/drafts/html/master/webappapis.html#queue-a-task
>
> A good concrete example here would be the WebSockets API:
>
> [[
> When the WebSocket connection is established, the user agent must *queue a
> task* to run these steps:
>  [...]
>  5. Fire a simple event named open at the WebSocket object.
> ]]
> http://www.w3.org/TR/websockets/#feedback-from-the-protocol
>
> Thanks to "queue a task" in the above excerpt, the "open" event will
> always fire after the current execution task is over, so you can create a
> WebSocket instance, start listening to the "open" event, and you know you
> will always catch the transition to "open".
>
> Conversely, when the spec does not use "queue a task" before "fire an
> event", it means that the event is to be dispatched synchronously.
>
>
>
>> 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.
>>
>
> I don't disagree but... it seems that the current consensus among
> implementers is that attaching an event listener should not trigger any
> side effect, and thus requiring the user agent to send an initial event
> when an event handler is attached is (currently, at least!) considered bad
> practice.
>
>
> [...]
>
>> 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?
>>
>
> Right, since this is considered an anti-pattern right now, I do not think
> that there exists a spec that uses it.
>
>>
>> Also, to explain the (slightly more complex) logic of the event source,
>> we could provide some sample code if needed.
>>
>
> It is worthwhile to document the pattern, provide sample code, and show
> how it improves the situation.
>
> Are there use cases that cannot be done without it? If yes, that could
> provide a good reason to shake things up. If not, I would make that fight
> in parallel to the development of the CG specs.
>
> Francois.
>

Received on Thursday, 11 June 2015 10:39:57 UTC