00:19 | <Steve Hicks> | (aside: I assume we did whatever we needed to to get on the agenda for next month?) |
00:28 | <Steve Hicks> | What about null vs "empty"? I think the important example to consider here is the dispatchSnapshot for an event dispatched directly from the event loop, rather than programmatically. I'll say upfront that I think having it be null for this case is desirable, since there's no good way to determine that some non-null value is "empty". But I can also see a consistency argument for it being HostGetTopLevelAsyncContextMapping(null) - this is the initial value for agentRecord.[[AsyncContextMapping]], and is therefore presumably its value at the exact time that the event is created/dispatched. So any other value (including null ) is presumably more complicated to spec (and also more challenging to polyfill). |
01:57 | <Andreu Botella> | Polyfills are a reason why I think, regardless of what we end up doing by default, there needs to be a way to have dispatchSnapshot be null |
01:58 | <Andreu Botella> | the event constructor's second argument is an options bag, that could be an additional option there |
02:02 | <Andreu Botella> | about challenging to spec, since we're considering a small initial rollout, events would have a null snapshot by default, with an option in the spec to use the current context instead |
02:03 | <Andreu Botella> | so distinguishing null vs the empty context would not be complicated, because only event dispatches that could have a causal context would be updated to use that option |
02:33 | <Steve Hicks> | What I was suggesting before was that we might not need to do a small initial rollout if we just scope it narrowly - instead, every event gets a dispatchSnapshot , and we handle other use cases with different properties. |
02:38 | <Andreu Botella> | wouldn't there still have to be a small initial rollout for those other properties? |
02:39 | <Steve Hicks> | maybe? but they would also be narrowly-scoped so it should also be possible to roll them out 100% |
02:39 | <Steve Hicks> | though they would only ever include a small subset of events |
02:40 | <Steve Hicks> | i.e. throwSnapshot only makes sense on error/rejection events |
02:41 | <Andreu Botella> | if we think of the future when we've achieved 100% rollout, I don't think the distinction between the different scopes will make sense |
02:43 | <Steve Hicks> | Ultimately the difference is in conceptualizing what fooSnapshot means. What I'm suggesting is that for any given foo you can look up exactly what it means, whereas the alternative is that you need to puzzle through "okay, this is a 'bar' event, so its fooSnapshot comes from when such-and-such happened" |
02:45 | <Steve Hicks> | it's an argument for homogeneity and comprehensibility. |
02:47 | <Andreu Botella> | for homogeneity, wouldn't that also have to apply to events which are dispatched synchronously from some API? el.click for example |
02:47 | <Steve Hicks> | i guess homogeneity is a matter of perspective, because the property names themselves would be heterogeneous, but the meaning of any given property is homogeneous |
02:47 | <Steve Hicks> | but yes, I would include those as well - el.click would set the dispatchSnapshot to the snapshot in which click was called. |
02:48 | <Andreu Botella> | why dispatchSnapshot? why not clickSnapshot? |
02:48 | <Steve Hicks> | every event gets a dispatch snapshot and it's always defined the same way - since click dispatches synchronously, it doesn't need a separate snapshot |
02:49 | <Andreu Botella> | but wouldn't you also have to look up what exactly that means for each event if you're trying to use it? |
02:51 | <Steve Hicks> | No, you just need to know whether the event is dispatched while executing ecmascript code or else by the event loop. If the former, it's always going to be whatever snapshot was active when the event started dispatching, and if the latter then it's always null (or possibly but hopefully not HostGetTopLevelAsyncContextMapping) |
02:52 | <Steve Hicks> | The bit about null is slightly problematic because as it's currently spec'd, the [[AsyncContextMapping]] isn't set to null during the event loop, so it would require a bit of special casing, which I'd rather avoid |
02:53 | <Andreu Botella> | if you're a developer trying to use the snapshot of an event for something, you would usually be writing code that works for multiple different event dispatches, and you'd also need to know what web API call dispatches the current event |
02:53 | <Andreu Botella> | I don't see how that's different for async dispatches |
02:53 | <Steve Hicks> | If the mapping lived on the execution context, it might be easier to make it null during the event loop |
02:56 | <Steve Hicks> | I assume you already know what event you're responding to, and you certainly know you're in an event handler at all, since you need to read something off of an event object in the first place. But that's what I was getting at before - are you more likely to just want whatever non-null snapshot you can get, or are you more likely to want fine-grained control to get the snapshot from a particular time? |
02:56 | <Steve Hicks> | And as I said above, I don't know the answer to that. |
02:57 | <Steve Hicks> | but userland code could easily maintain a mapping from event types to property names, if it wanted to |
02:58 | <Steve Hicks> | whereas putting that special casing directly in the engine (and polyfills) is unconditionally expensive and cuts off expressibility and understandability |
03:00 | <Andreu Botella> | Yes, you know you're in an event handler, and you know what event you're responding to. But if you're saying that for async dispatches developers might need to know which web API call's context is the dispatch snapshot, I don't see why that is not also the case for sync dispatches. The fact that the event is dispatched synchronously doesn't change anything other than possibly the current state of non-local variables the event listener might have access to. |
03:01 | <Andreu Botella> | You're registering the event in a different place from where it is dispatched in both cases |
03:02 | <Steve Hicks> | I think the difference is that async dispatches are inherently heterogeneous, whereas sync dispatches have a lot more in common between them. Also, async dispatches are much more likely to prefer the default registration context anyway. |
03:04 | <Steve Hicks> | One theory I have is that one-time callbacks are more likely to want registration-snapshot, while multiply-called callbacks are more likely to want a causal snapshot. I also believe async dispatches tend to correlate more with one-time listeners. |
03:05 | <Steve Hicks> | (this isn't a hard rule, of course - abort event is one-time and synchronous, for instance) |
03:07 | <Steve Hicks> | But what it sounds like you're worried about is an async-dispatch event where neither the registration snapshot, nor whatever synchronous dispatch snapshot if there happens to be one, is correct - but I can't come up with any examples of that. |
03:09 | <Steve Hicks> | i mean, i guess that's the point of the follow-ups - throwSnapshot, or sendSnapshot, etc |
03:09 | <Andreu Botella> | I don't think there would be any cases where an event would have both non-null synchronous and asynchronous dispatch snapshots |
03:09 | <Steve Hicks> | but again, I think those are niche cases where you probably know exactly what you need |
03:12 | <Andreu Botella> | One theory I have is that one-time callbacks are more likely to want registration-snapshot, while multiply-called callbacks are more likely to want a causal snapshot. I also believe async dispatches tend to correlate more with one-time listeners. |
03:12 | <Andreu Botella> | the event listener knows that far better than the browser/spec |
03:14 | <Andreu Botella> | I can see how for error and unhandledrejection, using the same property might be confusing, and maybe there it makes sense |
03:15 | <Andreu Botella> | huh, I think I see where the disconnect between us is |
03:16 | <Andreu Botella> | you're seeing the whole of XHR boilerplate as one single thing that you do to set up the XHR events |
03:16 | <Andreu Botella> | and so creating the XHR object vs xhr.load() vs xhr.send() are just parts of that process |
03:16 | <Andreu Botella> | and you'd have to look up exactly what part of that causes the event |
03:18 | <Andreu Botella> | but in that case, is there any need for developers to distinguish between those snapshots, since they'll almost always happen in the same context? |
03:19 | <Steve Hicks> | right, which is also the same context that the listener is registered in |
03:19 | <Andreu Botella> | well, I think you can reuse an XHR object for multiple connections |
03:19 | <Steve Hicks> | so in most cases, no, the difference is irrelevant - but it's still a difference that people just won't know or have a good sense of |
03:19 | <Andreu Botella> | but that's not commonly done |
03:19 | <Steve Hicks> | you can |
03:20 | <Steve Hicks> | and in that case, the distinction would matter |
03:20 | <Andreu Botella> | the distinction between registration and load/send, yes, but not between load and send |