13:11
<Andreu Botella>

I was thinking that the unhandled rejection context here is "foo", not "baz":

asyncVar.run("foo", main);

async function main() {
	await asyncVar.run("bar", async () => {
		await asyncVar.run("baz", async () => {
			throw new Error();
		});
	});
}
13:11
<Andreu Botella>
so shouldn't it be the same for sync unhandled errors?
13:29
<littledan>
I don't understand what you're getting at; the async/await case is different since it corresponds to a bunch of nested try/catch/rethrow patterns.
13:30
<Andreu Botella>
will that be obvious to developers?
13:31
<littledan>
no, developers will expect that the inner place where the error inside the async/await is the source. But making it "also broken" for sync exceptions won't fix that expectation.
20:29
<Steve Hicks>
Are we talking about the callback's context or the one that's hung on the unhandledrejection/error event? If the former, I'm arguing it should always be registration context. Would it be possible for event.errorSnapshot to be "baz" even for the unhandledrejection case?
20:32
<Steve Hicks>
I've come around to the view that one of the advantages of heterogeneous access to causal contexts (which I was arguing against a few weeks ago) is that you can target more clearly exactly which context you get.
20:32
<littledan>
I think we're talking about the supplemental context that exists as a property
20:32
<littledan>
even for that there's a lot of decisions to make!
20:33
<Steve Hicks>
yes there are
20:33
<littledan>
we could expose a whole bunch of them, but IMO one of them will probably be good enough to start, and people won't really be so great at choosing between a bunch of them anyway
20:33
<Steve Hicks>
so i guess i'm asking, is there a reasonable way to spec it so that the unhandled rejection is also baz?
20:34
<Steve Hicks>
right, I don't think we should expose a ton
20:34
<littledan>
so i guess i'm asking, is there a reasonable way to spec it so that the unhandled rejection is also baz?
I have a huge amount of trouble understanding how that'd work. BUT we could make it so that error objects have a causal context when they're allocated or thrown for the first time, and maybe that'd solve most of the problem.
20:35
<Stephen Belanger>
IMO error construction should capture a context snapshot and you can read out of that later by accessing some property on that error.
20:35
<littledan>
jinx
20:35
<Stephen Belanger>
Yeah.
20:35
<littledan>
(I prefer allocated better than first thrown too)
20:35
<Steve Hicks>
i think that's a good balance
20:36
<Stephen Belanger>
First thrown is a bit mysterious but possibly more correct. You need extra logic to detect if it already HAS a context on a rethrow though.
20:36
<littledan>
first thrown is sad-feeling because it means mutating an object. what if the same thing is thrown twice? anyway that is not very pragmatic motivation, it's my theorist side talking
20:37
<Stephen Belanger>
Yeah. Almost makes more sense to not be attached to the Error object, but then where would you put it?
20:37
<littledan>
but we might still need a causative context for unhandled rejections in case they aren't error objects
20:38
<Stephen Belanger>
True.
20:38
<littledan>
Yeah. Almost makes more sense to not be attached to the Error object, but then where would you put it?
it could be on the event (but still, requires extra machinery in implementations)
20:38
<Stephen Belanger>
For unhandledRejection we probably would need the point it throws and not an Error property given you can throw non-errors and unfortunately sometimes people do. 😐
20:39
<Steve Hicks>
unhandledrejection isn't dispatched synchronously, so it's a little more awkward
20:39
<Steve Hicks>
ideally it could reuse the same mechanism for synchronous events (i.e. dispatchEvent captures the snapshot)
20:40
<littledan>
as we've previously reasoned, we could either store the context when the Promise is allocated (as in Node) or rejected (as in the current spec). I guess in either case, it could be in the Promise itself, and then it's only read by the unhandled rejection thing.
20:41
<Stephen Belanger>
Capturing on dispatchEvent also means we could have anything be able to access both register time and call time contexts. 🤔
20:41
<littledan>
I previously convinced myself that the rejected-time context is definitely way better, but now, with distance, I could see that either could be OK...
20:41
<littledan>
Capturing on dispatchEvent also means we could have anything be able to access both register time and call time contexts. 🤔
lots of stuff isn't dispatchEvent...
20:41
<littledan>
unless you mean the internal equivalent
20:41
<Stephen Belanger>
Yes, internal equivalent.
20:42
<Steve Hicks>
reject-time means you're not making as many unused snapshots
20:42
<littledan>
Yes, internal equivalent.
yeah so conversations with DOM people like Anne seemed to point to, we'd only get this little by little, for particular things where it made sense, since it's complicated and depends on that particular API's details
20:42
<littledan>
reject-time means you're not making as many unused snapshots
but "making a snapshot" is copying a pointer
20:43
<littledan>
we're not doing an allocation, in particular
20:43
<Steve Hicks>
is there a concern about lifetime management?
20:43
<littledan>
is there a concern about lifetime management?
well, IMO we should give a fresh object identity for the snapshot each time you use it, so we don't have to worry about that
20:43
<littledan>
via the getter
20:43
<Stephen Belanger>
yeah so conversations with DOM people like Anne seemed to point to, we'd only get this little by little, for particular things where it made sense, since it's complicated and depends on that particular API's details
I mean…that’s possibly fine? We just need to make a bunch of individual cases for each API, which it seemed like Andreu was already doing the research work for?
20:43
<littledan>
or maybe that's too weird and un-getter-like?
20:44
<littledan>
the alternative is, you eagerly stash a pointer to the underlying data structure, but somehow you can dynamically type check that, and on first access, you replace it with a snapshot in place, and subsequent accesses notice that it's already a snapshot
20:45
<Steve Hicks>
well, IMO we should give a fresh object identity for the snapshot each time you use it, so we don't have to worry about that
Not sure we're talking about the same thing? IIUC there was concern about when a snapshot was no longer reachable (hence all the explicit resource management discussions) and if we've got a never-will-be-used snapshot hanging on a long-lived promise, that could confuse it
20:45
<littledan>
I mean…that’s possibly fine? We just need to make a bunch of individual cases for each API, which it seemed like Andreu was already doing the research work for?
yes, though there's still the question of, "are we doing this now or 'later'". IMO we should identify the cases where we believe it's needed now (e.g., some of this error stuff) and document why it's needed, and expect that the initially shipped version doesn't include so many of these
20:46
<littledan>
Not sure we're talking about the same thing? IIUC there was concern about when a snapshot was no longer reachable (hence all the explicit resource management discussions) and if we've got a never-will-be-used snapshot hanging on a long-lived promise, that could confuse it
oh oops I was talking about a superficial object identity issue, totally separate. For lifetime.... yeah just leak?
20:46
<littledan>
I don't see a solution to this lifetime question
20:46
<Stephen Belanger>
Selfishly, APM’s probably won’t care as we’re focused on servers where most of those APIs don’t exist anyway. 😅
20:47
<littledan>
well, some people care about client-side performance monitoring, but I take it that's not your team
20:47
<littledan>
I guess the lifetime thing might be especially bad for something like Error objects, where it might be really non-obvious what you're keeping around
20:48
<Steve Hicks>
you could have HostPromiseRejectionHandler add/remove it?
20:48
<littledan>
and you can imagine keeping errors around
20:48
<littledan>
you could have HostPromiseRejectionHandler add/remove it?
it would modify the error?
20:48
<Steve Hicks>
well, some people care about client-side performance monitoring, but I take it that's not your team
this is our use case
20:49
<Steve Hicks>
it would modify the error?
no, the promise
20:50
<littledan>
oh, right... yeah that makes sense, the unhandled rejection can only happen once. (In fact, maybe the spec doesn't even say any of this, since it's implied by unreachability.) But this doesn't handle the error issue.
20:50
<Justin Ridgewell>
For unhandledRejection we probably would need the point it throws and not an Error property given you can throw non-errors and unfortunately sometimes people do. 😐
I’d honestly be happy saying that’s an anti-pattern and you don’t get the correct context in this case.
20:50
<Justin Ridgewell>
as we've previously reasoned, we could either store the context when the Promise is allocated (as in Node) or rejected (as in the current spec). I guess in either case, it could be in the Promise itself, and then it's only read by the unhandled rejection thing.
I don’t think this helps in Andreu’s code sample, unless we’re expecting the ”foo” context
20:50
<littledan>
I’d honestly be happy saying that’s an anti-pattern and you don’t get the correct context in this case.
actually we care a lot about this case at Bloomberg, but maybe for reasons which don't deserve to be prioritized
20:51
<littledan>
I don’t think this helps in Andreu’s code sample, unless we’re expecting the ”foo” context
that's right, I'm discussing strategies for getting "foo"
20:51
<Steve Hicks>
Google also cares about throwing non-errors, but we believe the solution is "Don't do that" rather than bloating the standards to account for it
20:52
<littledan>
well, maybe we can look into the "don't do that" path... maybe Chengzhong can follow up on this?
20:52
<littledan>
Chengzhong Wu: ^
20:53
<Steve Hicks>
well, maybe we can look into the "don't do that" path... maybe Chengzhong can follow up on this?
(aside) in particular, one strategy we're consider is to transpile all throw xs to throw trackNonError(x) that throws a real error with a stack trace asynchronously so as to at least get some logging in place to identify all the offenders
20:54
<Justin Ridgewell>
Are you suggesting we attach the context onto the error instead of just running the unhandledRejectionHandler in the correct context?
20:54
<Steve Hicks>
Are you suggesting we attach the context onto the error instead of just running the unhandledRejectionHandler in the correct context?
Yes?
20:55
<Steve Hicks>
at that point, maybe there's not as much of a reason to even attach a context to unhandledrejection?
20:55
<littledan>
Are you suggesting we attach the context onto the error instead of just running the unhandledRejectionHandler in the correct context?
well, we're somewhat deep into this thread of "run everything in registration context, and the 'correct' context is off to the side somewhere", and so we're trying to reason about whether the error's context is enough, or if we also need the rejection's context
20:55
<littledan>
if we get the rejection's context, it could be exposed in the event
20:57
<Steve Hicks>
I'm still hopeful that maybe we can get a more general resolution context for promises, but my understanding is that littledan has reason to think that's not feasible
20:58
<littledan>
I think a general resolution context for all promises would be seen as an information leak, but again that's not a very pragmatic reason for anything. Definitely it's easier to just capture the context where resolve()/reject() was done, rather than have that somehow be propagated across .then() as we've previously discussed.
21:02
<littledan>
with the semantics I'm picturing for capturing causal contexts for all Promises, for async functions, the resolve/reject context would be boring: it would be the context when the async function was called
21:03
<littledan>
it would only be something different if you call the promise constructor/withResolvers
21:06
<Chengzhong Wu>
well, maybe we can look into the "don't do that" path... maybe Chengzhong can follow up on this?
I am not a fan of granting "throw" super power in spec to recover stacks and contexts either. Definitely, people can throw primitive values, just don't do that
21:07
<littledan>
I am not a fan of granting "throw" super power in spec to recover stacks and contexts either. Definitely, people can throw primitive values, just don't do that
OK, but how does this apply to unhandled rejections?
21:08
<Chengzhong Wu>
I think it could be a property of PromiseRejectionEvent/ErrorEvent, as documented in https://github.com/tc39/proposal-async-context/pull/94/files#diff-85367a6a792209cf5826726990ddd1f0fd7a572bac4162097bc5e7e192aa625cR295, updated today
21:09
<Chengzhong Wu>
Well, I think this could be definitely in its own piece of document expansion.
21:09
<littledan>
isn't putting a context in ErrorEvent giving those superpowers?
21:10
<Chengzhong Wu>
ErrorEvents are errors captured at the host level
21:10
<Chengzhong Wu>
"throw" itself doesn't have this superpower
21:10
<Chengzhong Wu>
so this won't have a re-throw problem
21:38
<Steve Hicks>
Hosts already compute stack traces at error construction time, so it seems pretty reasonable to also copy the context pointer at the same time. But I'm a little fuzzy on how this proposal interacts with that, since EcmaScript doesn't even mention stack anywhere, even non-normatively.
21:39
<Steve Hicks>
Where are we planning on documenting the various DOM interactions?
21:39
<Chengzhong Wu>
Hosts already compute stack traces at error construction time, so it seems pretty reasonable to also copy the context pointer at the same time. But I'm a little fuzzy on how this proposal interacts with that, since EcmaScript doesn't even mention stack anywhere, even non-normatively.
it's being worked at https://github.com/tc39/proposal-error-stacks.
21:40
<littledan>
Where are we planning on documenting the various DOM interactions?
we'll need to do so in places linked from https://github.com/whatwg/html/issues/10432 . I believe Andreu Botella is working on a document for this.
21:42
<littledan>
it's being worked at https://github.com/tc39/proposal-error-stacks.
"Being worked on" is generous for this proposal; the champions do not plan to work in a way that meets the (reasonable, IMO) requirements of browsers for spec work in this area. So we should definitely not depend on it.
21:42
<littledan>
I don't think there's active work in that repo either, given that browsers expressed requirements and that was demotivating
21:43
<Chengzhong Wu>
😅 "stalled for years"
21:48
<littledan>
yeah I think if you don't really get to make a big point about this happening "for years" when it's a reasonable thing requested and you just decided to not do it. "stalled for years" implies that something is actually happening.
22:03
<Stephen Belanger>
well, some people care about client-side performance monitoring, but I take it that's not your team
Not my team, but also client monitoring has not generally had that level of capability yet, so it’s still a feature add even without all the things covered. Of course completeness is the ideal, but it’s a difficult spec so I can see it being hard to cover everything at once.
22:35
<littledan>
client monitoring in general? I'm curious whether Steve Hicks 's effort is for refining present monitoring, or making a new system that doesn't exist yet.
22:38
<littledan>
My goal is to build a reasonable basis for client-side monitoring, not reaching either extreme of "focus on server side now, do client side later" or "expose everything interesting that goes on in the client"