Skip to content
Open
33 changes: 29 additions & 4 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -2556,12 +2556,37 @@ as it implies that it's possible to dispatch an event asynchronously.
All events are dispatched synchronously.
What is more often implied by "asynchronous event" is to defer firing an event.

<h3 id="state-and-subclassing">Use plain {{Event}}s for state</h3>
<h3 id="state-and-subclassing">Put state on {{Event/target}} objects rather than {{Event}}s</h3>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous heading seemed… incomplete.


Where possible, use a plain {{Event}} with a specified `type`,
and capture any state information in the {{Event/target}} object.
Put state on the {{Event/target}}
and use events to signal updates to that state,
rather than having state on {{Event}} objects.

It's usually not necessary to create new subclasses of {{Event}}.
<div class="note">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be a note?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it to separate the rule from the explanation of the rule, since the explanation is non-normative. But this spec doesn't have such a strict divide between normative and non-normative. But I don't mind either way, happy to go with whatever you think.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I understand this paragraph...

Or if it's completely accurate. There are a few events that are fired asynchronously, e.g. slotchange. The state on the event target might have been updated when the event listener is triggered.

Having state on the {{Event/target}} object can used to determine the current state,
without waiting for the next event.
This is particularly useful if there's a final state,
where there will be no further events.
</div>

It's usually not necessary to create new subclasses of {{Event}},
but they can be used to provide information relating to how the state change occurred.

<div class="example">
Properties on {{HTMLInputElement}},
such as {{HTMLInputElement/value}},
provide the state of the input.
Properties on {{InputEvent}},
such as {{InputEvent/inputType}},
describe the nature of an update to the state.
</div>

In some exceptional cases, where maintaining state on an object is expensive,
the state may be placed on the {{Event}} instances,
allowing {{EventTarget/addEventListener}} and {{EventTarget/removeEventListener}} to signal interest/disinterest in this state.
Although, other patterns should be considered,
such as retuning an {{EventTarget}} from a function,
where the function call is a similar signal of interest.
Comment on lines +2587 to +2589
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that I'm following this sentence. I don't know what "retuning" means in this context.

Do you mean that people might want to consider adding a function to EventTarget to get the state instead? Or are you saying that you might add a function that flips the EventTarget into a mode that tracks the state, so that the cost is only paid when the state is needed?

It would really help if there was a concrete example of this problem. I'm having a little trouble coming up with one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling the function returns the EventTarget, at which point the implementation will have to track the state (if it's exposed on the target instead of the event).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, so instead of something like:

self.pageDataUsage.addEventListener('change', () => {
  console.log(self.pageDataUsage.bytesReceived);
});

…where self.pageDataUsage.bytesReceived will need to be continually updated so it can be synchronously accessed by any code running in the doc/worker, you'd do something like:

self.pageDataUsage.addEventListener('change', (event) => {
  console.log(event.bytesReceived);
});

…where the data is only available to listeners of the change event, so if there are no listeners, the underlying operation to get bytesReceived doesn't need to happen. Or, alternatively, "return an {{EventTarget}} from a function":

const pageDataUsage = await self.monitorDataUsage();

pageDataUsage.addEventListener('change', () => {
  console.log(pageDataUsage.bytesReceived);
});

In this case, it's the call to monitorDataUsage() that signals interest, and GC of pageDataUsage will signal disinterest.

The second model gives you an object representing current state that can be passed around.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

retuning is a typo in the PR. I'll fix that once we figure out the right wording.


<h3 id="events-vs-observers">Use Events and Observers appropriately</h3>

Expand Down