01:50
<Steve Hicks>
Would we expose any way to access the flow context from a callback? It may not be particularly relevant from most DOM APIs, but Promise's resolution context is quite relevant, and I worry about userland APIs from a consistency standpoint (either they'll use Snapshot.wrap for consistency's sake with all the builtins, or else they cause a lot of confusion). And many users of userland APIs (signals, etc) will want access to the flow context. If all we have is lexical context for everything, it's not particularly useful for us, nor for APM - I'm not sure who the user would be at that point.
06:55
<littledan>
Would we expose any way to access the flow context from a callback? It may not be particularly relevant from most DOM APIs, but Promise's resolution context is quite relevant, and I worry about userland APIs from a consistency standpoint (either they'll use Snapshot.wrap for consistency's sake with all the builtins, or else they cause a lot of confusion). And many users of userland APIs (signals, etc) will want access to the flow context. If all we have is lexical context for everything, it's not particularly useful for us, nor for APM - I'm not sure who the user would be at that point.
Yes, I think (at a minimum) we should do this for a number of Events, make an extra property which is an AsyncContext.Snapshot taken from when it was resolved. So this would be for "lexical" variables as well.
06:56
<littledan>
then APMs can patch all entry points to set event listeners to watch for a particular variable on that other snapshot and do something with it
07:22
<littledan>
Yeah, agreed that two type of variables could be a solution. Did you have a chance talking about run(value, fn) vs set(value) styles? Would it be too conservative to have strong scope guarantee in Web API designs?
We didn’t discuss this; I will mention it. But I don’t know if I can represent the arguments for set() well, and I am pretty sure this all would not form much of an interesting argument in favor of flow-through.
17:16
<Steve Hicks>
Yes, I think (at a minimum) we should do this for a number of Events, make an extra property which is an AsyncContext.Snapshot taken from when it was resolved. So this would be for "lexical" variables as well.
I really dislike finding heterogeneous places to put the flow snapshot for each different system. What about promises? Assuming await and Promise.then are both lexically bound (which I mostly agree with - the former somewhat moreso than the latter, but overall I think it makes sense), there's no way to get at the promise's resolution context, and that's very relevant for understanding "follows from". Would we expose some sort of static function to grab it? Promise.resolutionSnapshot(promise)? Snapshot.fromPromise(promise)? Could something like that work more generally for other cases like events?
17:20
<Steve Hicks>
Assuming promises store their resolution context somewhere, is there a reasonable way to preserve it when operating on the promise? I'm imagining wanting to wrap a function fn such that Snapshot.from(p1.then(fn)) === Snapshot.from(p1) and Snapshot.from(p2.then(fn)) === Snapshot.from(p2)... but without having access to the promise itself (just the resolved value), I don't see any way to write such a function.
17:21
<Steve Hicks>
This seems to still argue for something more general, like a nullary Snapshot.cause() or something
17:22
<Steve Hicks>
(which then sends us back down the rabbit-hole of whether this is a stack of contexts or just a back-and-forth previous context, etc)
17:27
<Steve Hicks>
Finally, I do think it's worth seriously considering the likely real-world ramifications of the default-lexical decision in terms of what concrete code will be written in the 1-2 years after the standard lands. Where will pepole be adding wrap() calls, and where will people be digging up causal snapshots, etc? Would having rolled all that out cause undue problems in the future if we decided to make a FlowVariable, since now everybody is mucking with these global snapshots everywhere, which (as Stephen Belanger points out) is the thing you shouldn't be doing in order for the flow context to actually do the right thing and not get wrecked by intermediate subtask calls.
21:16
<littledan>
I suggest we come up with a common name to use for the instance method/getter where the originating context is
21:18
<littledan>
My preference is that promises, in particular, would not maintain this information, at least at first. We could consider adding it later, but it might be more burden to implement than its value
21:19
<littledan>
There are a few cases where it is very important to present this information, eg unhandled rejections
21:19
<littledan>
Finally, I do think it's worth seriously considering the likely real-world ramifications of the default-lexical decision in terms of what concrete code will be written in the 1-2 years after the standard lands. Where will pepole be adding wrap() calls, and where will people be digging up causal snapshots, etc? Would having rolled all that out cause undue problems in the future if we decided to make a FlowVariable, since now everybody is mucking with these global snapshots everywhere, which (as Stephen Belanger points out) is the thing you shouldn't be doing in order for the flow context to actually do the right thing and not get wrecked by intermediate subtask calls.
Yes, it is important to analyze this. How should we do so?
21:21
<Steve Hicks>
Right, so we already need to keep track of resolution context for rejected promises - do you anticipate it's much harder to keep for all promises?
21:23
<Steve Hicks>
I don't know how far a common name will get us. It only makes sense when you've got an object to look it up on. What about mutation observers? Its callback is passed an array and the observer itself, neither of which has a convenient place to hang a flow snapshot.
21:25
<Steve Hicks>
The underlying concept of "what was the context before this callback was run?" is pretty general and has no specificity to events (though it's not quite the right concept for restoring the resolution context of promises...) - so it seems a bit unnatural to tie it to that.
21:27
<littledan>
I don't know how far a common name will get us. It only makes sense when you've got an object to look it up on. What about mutation observers? Its callback is passed an array and the observer itself, neither of which has a convenient place to hang a flow snapshot.
Isn’t it an array of objects? Each of these objects could have the property
21:29
<Andreu Botella>
Right, so we already need to keep track of resolution context for rejected promises - do you anticipate it's much harder to keep for all promises?
the way this currently works in the spec and in V8 is that an embedder function is called synchronously when the unhandled rejection happens
21:29
<Andreu Botella>
and that function then enqueues a task on the event loop that fires the event
21:29
<Andreu Botella>
so it happens outside of the JS engine
21:30
<littledan>
Right, so we already need to keep track of resolution context for rejected promises - do you anticipate it's much harder to keep for all promises?
I would sort of assume that, for built-in methods, we would generally use the “registration-time” context for that. But then this makes me wonder if we should generally return to the Node.js ALS semantics where rejection is always reported to the allocation-time context…. Now I confused myself
21:30
<littledan>
the way this currently works in the spec and in V8 is that an embedder function is called synchronously when the unhandled rejection happens
How does this collect the right context? What if no JS is on the stack?
21:31
<Andreu Botella>
for a promise-returning web API, you mean?
21:32
<Andreu Botella>
the context would be set when rejecting the promise
21:56
<littledan>
the context would be set when rejecting the promise
Sure but there might not be JS on the stack when rejecting the promise, so at some point earlier you need to save off the context
23:05
<Steve Hicks>
You lost me with the bit about allocation-time. I think polyfilling the causal snapshots is going to be a nightmare, but I guess I can maybe see an argument for exposing these different causal snapshots in different ways if they end up meaning different things. That said, I know for sure that signals will want access to causal context and there's no event object there - we should consider what precedent this sets and what options would be open to that proposal to be consistent.