04:07
<Bakkot>
jmdyck: you had a comment on https://github.com/tc39/ecma262/issues/828 today which disappeared
04:07
<Bakkot>
did you delete it on purpose, or is github broken?
04:08
<jmdyck>
on purpose.
04:08
<jmdyck>
meant to cancel, but comment-and-close button is in the same place
04:08
<Bakkot>
yeah, that UI is not great
15:55
<jorendorff>
devsnek: hey, i don't know if you saw, but yulia and I asked for a slot on the agenda for iterator helpers next week
15:55
<devsnek>
jorendorff: yeah yulia told me 👍🏻
15:55
<jorendorff>
devsnek: ok, cool, I want to make sure there are no surprises for you in this presentation
15:56
<jorendorff>
the goal is to get committee approval for a specific specification approach
15:56
<devsnek>
how did you end up feeling about
15:56
<devsnek>
https://github.com/tc39/proposal-iterator-helpers/issues/86#issuecomment-632768983
15:58
<jorendorff>
devsnek: I like it. I think the specification ends up shorter and cleaner that way
15:58
<devsnek>
🎉
15:58
<jorendorff>
than if we make every one of these its own iterator "class" with three methods
15:59
<jorendorff>
it's not drastically shorter and cleaner but the difference is real, fewer steps, fewer spec sections
15:59
<devsnek>
and it handles state cleanly
16:00
<jorendorff>
devsnek: is it OK to say in a slide that the idea is to pick an approach now, and try for Stage 3 next time?
16:00
<devsnek>
yeah definitely not time for stage 3 yet
16:01
<jorendorff>
👍 ok
16:02
<jorendorff>
devsnek: the champions will be ok with this?
16:09
<devsnek>
they were fine with generators at least
16:10
<devsnek>
i'll ping them
19:15
<bradleymeck>
has anyone attempted to propose a non-propagating promise handler method? e.g. `Promise.prototype.handle(fn, errfn): void`
19:15
<bradleymeck>
i don't see any refs on a quick glance
19:16
<TabAtkins>
bradleymeck: What's the difference between `.handle()` and a `.then()` that you just ignore the return value of?
19:17
<bradleymeck>
no return value, can't do the evil unhandledRejection dance that .then has, likely wouldn't swallow the error
19:17
<TabAtkins>
Ah, hm, kk.
19:18
<bradleymeck>
https://twitter.com/bradleymeck/status/1266060854111408129
19:18
<bradleymeck>
see thread leading up to that
19:18
<bradleymeck>
basically unused return position of .then is a propagation point for unhandledRejection
19:19
<TabAtkins>
yup, got it.
19:23
<devsnek>
bradleymeck: where do errors thrown in `fn` go
19:25
<bradleymeck>
i presume it just lets it propagate through, since it is on a new tick that would be to w/e the global exception handler is
19:25
<bradleymeck>
i certainly have written `p.catch(e => setTimeout(() => {throw e;}))` before and it would go through same path
19:26
<bradleymeck>
right now i don't think there is a way to escape the promise swallowing state in raw JS itself?
19:26
<devsnek>
so host hook
19:26
<bradleymeck>
not a new one
19:26
<ljharb>
bradleymeck: what would happen if the handle function threw
19:26
<devsnek>
HostHandleErrors or whatever its called
19:26
<ljharb>
that'd still be an unhandled rejection
19:26
<bradleymeck>
ljharb: it isn't rejecting a promise, it is just throwing
19:26
<ljharb>
bradleymeck: right but sync or async
19:26
<bradleymeck>
idk what you mean
19:26
<devsnek>
oh the report errors hook was removed
19:27
<ljharb>
like how would it throw an exception
19:27
<ljharb>
it'd just be uncatchable until a global uncaught exception hook?
19:27
<ljharb>
one that no JS env is required to provide?
19:27
<devsnek>
sure
19:28
<ljharb>
that sounds pretty bad to me
19:28
<bradleymeck>
ljharb: i mean, you can always wrap stuff in try/catch
19:28
<bradleymeck>
this is the same behavior as... many things
19:28
<devsnek>
bradleymeck: thinking a bit more, maybe something like `.then().catch().join()`
19:29
<ljharb>
not if it's not synchronous
19:29
<devsnek>
or `.end()` as a less biased name
19:29
<bradleymeck>
ljharb: it shouldn't be sync
19:29
<ljharb>
(`.done` is the one with precedent, iirc)
19:29
<bradleymeck>
promise state cannot be inspected sync
19:29
<ljharb>
bradleymeck: right but you can't wrap everything inside a function that can throw in try/catch
19:29
<bradleymeck>
ljharb: ?
19:29
<ljharb>
and that boilerplate is not better than "remembering to chain promises"
19:30
<bradleymeck>
what do you mean you can't put try/catch around a function
19:30
<ljharb>
the `handleFn`, it could throw in default args, or inside a `catch` block
19:30
<ljharb>
you can put it around a sync function call
19:30
<ljharb>
you can't put it around `p.handle(handleFn)`
19:30
<bradleymeck>
that isn't any different from other promise handlers
19:30
<devsnek>
i think ljharb is saying
19:30
<devsnek>
the problem is missing exceptions
19:30
<ljharb>
the other promise handlers always produce a new promise, and thus an opportunity to handle the error
19:31
<devsnek>
so adding something that still misses exceptions
19:31
<ljharb>
and thus a possible unhandled rejection
19:31
<devsnek>
doesn't fix the problem
19:31
<ljharb>
adding a promise method that doesn't produce a new promise just solves one problem by creating a larger one
19:32
<bradleymeck>
ljharb: the person attaching the handler is responsible for the errors in their handler. I don't understand.
19:32
<devsnek>
isn't that true at any level
19:32
<bradleymeck>
thats the whole reason you end up with process.nextTick style rethrows in node
19:33
<ljharb>
the person calling `.then` is responsible for handling the new promise that's produced. how is that different
19:33
<bradleymeck>
it isn't which is why I'm very confused
19:34
<bradleymeck>
the behavior delegation is still on the person calling the method to handle
19:34
<bradleymeck>
one you have to wrap the returned promise is you want to do things, the other you wrap the handler
19:34
<ljharb>
oh sure. if a method returns a promise. then the caller is responsible for handling that promise's possible rejection
19:34
<bradleymeck>
if you wrap the handler you can 100% guarantee in both cases no "unhandled" events occur
19:34
<ljharb>
the handler might produce a rejected promise tho
19:35
<bradleymeck>
not if you wrap that handler
19:35
<ljharb>
how do you "wrap" that in a way that's not the same problem you're trying to avoid?
19:35
<ljharb>
`new Promise((resolve) => handler())` wraps it, sure, but then you have another potential rejection to handle
19:35
<bradleymeck>
p.then(v=> {try { return f(v) } catch (e) {} }, () => {})
19:35
<bradleymeck>
i mean... you *could* write that
19:36
<ljharb>
how is that better than `p.then(f).catch()`?
19:36
<devsnek>
`.catch(() => {})`*
19:36
<ljharb>
no need
19:36
<ljharb>
you can even do that as the async function, and prevent your consumer from having possible unhandled rejections
19:36
<ljharb>
devsnek: p sure `.then()` and `.catch()` work with no args
19:37
<bradleymeck>
i mean that snippet above wouldn't be, but a .handle or something seems to have less need for all of this wrangling of promise propagation of errors
19:37
<bradleymeck>
you just asked how you could do it and i gave a way
19:37
<ljharb>
right, i'm saying that it seems like it moves the need around at best
19:38
<ljharb>
like you had your `p.handle(handler)` above. how do you deal with the asynchronous rejection if `handler` produces a rejection?
19:38
<devsnek>
ljharb: if you leave out the argument to catch it defaults to `(v) => throw v`
19:38
<bradleymeck>
it creates fewer promises and doesn't have the error swallowing behavior since global hooks for errors do exist without the tick heuristic involved either. it is just a way of expressing intent
19:38
<ljharb>
devsnek: oops, you're right
19:38
<bradleymeck>
ljharb: handler cannot produce a rejection, the function can throw
19:38
<bradleymeck>
there is no place for the rejection to reject
19:38
<ljharb>
ps, rofl, i typed `Promise.reject(3).` in the chrome console and immediately got an unhandled rejection warning
19:39
<devsnek>
its not wrong
19:39
<ljharb>
sure it is, those warnings aren't supposed to appear synchronously, nor before i hit enter
19:39
<ljharb>
if it's eagerly evaluating as i type for UX reasons it should be suppressing the warnings until i hit enter
19:39
<ljharb>
bradleymeck: handler will be invoked asynchronously
19:39
<bradleymeck>
unhandledRejection is a heuristic thing
19:40
<ljharb>
right but until i hit enter in the repl the JS engine has no way to know what i'm typing
19:40
<bradleymeck>
ljharb: yes? like nextTick, setImmediate, setTimeout, a lot of events, etc.
19:40
<ljharb>
bradleymeck: if handler throws asynchronously, there is no way to catch that
19:40
<ljharb>
bradleymeck: none of those are in JS.
19:40
<bradleymeck>
ljharb: correct? but you can't handle a dropped promise ref either
19:41
<ljharb>
wait i'm confused now, which thread is that part of
19:41
<ljharb>
indeed, i can't handle a rejected promise when the promise is no longer reachable
19:41
<bradleymeck>
i'm only talking about a .handle
19:41
<bradleymeck>
my argument is unhandled rejection is a different thing than unhandled exception
19:42
<bradleymeck>
and you currently only have a way to propagate unhandled rejection in JS
19:42
<ljharb>
i agree with that, notably in node where i don't think an unhandled rejection should crash the process :-)
19:44
<devsnek>
bradleymeck: in any case, i think anything that requires a change in behaviour
19:44
<devsnek>
is not great
19:44
<bradleymeck>
devsnek: change in behavior?
19:44
<devsnek>
since the problem is forgetting to do exception handling
19:44
<devsnek>
the solution can't involve assuming people don't forget to do something
19:44
<bradleymeck>
i don't think we can state we can prevent forgetting exception handling
19:45
<bradleymeck>
i'm more interested in the fact that only providing a fulfillment handler even if the promise rejects is stuck in an odd place
19:45
<devsnek>
honestly i think the solution will be
19:45
<devsnek>
some form of async context thing
19:46
<devsnek>
just wrap your app in an async context
19:46
<bradleymeck>
so async context is a bit different
19:46
<devsnek>
one of the things it can do is tell where a rejected promise came from
19:46
<bradleymeck>
there is a different between unhandled rejection which by language default is a noop even if pretty much all hosts have moved it to an event
19:47
<bradleymeck>
and unhandled exception which is the form that propagates as some form of error in all the environments
19:48
<devsnek>
right
19:49
<bradleymeck>
so, even if you wrap things in some kind of context, you still want to disambiguate that
19:51
<devsnek>
uncaught exceptions rip through async context
19:55
<bradleymeck>
well you can't make an uncaught exception once you are in promise hell
19:55
<bradleymeck>
at least not in pure js
19:56
<devsnek>
i'm not sure what we're talking about anymore lol
19:56
<devsnek>
i was just saying you could do
19:56
<devsnek>
`new AsyncContext().onError(error handler)`
19:57
<bradleymeck>
i'm stating that if your async context is started up, ala something like otherPromise.then(doThingInAContext) doThingInAContext can never create a unhandled exception in JS
19:58
<devsnek>
well the error gets handled in the async context's error handler
19:58
<devsnek>
which isn't a promise reaction
19:58
<devsnek>
so if that throws its not a rejection
19:59
<bradleymeck>
what do you mean
19:59
<bradleymeck>
how does it get out of being in the promise handler position and thus a rejection position
20:03
<devsnek>
bradleymeck: the async context's error handler isn't a promise reaction
20:03
<devsnek>
idk how else to say that
21:30
<ljharb>
gut checkL can step 3.e.ii ever fail to define the property? https://tc39.es/ecma262/#sec-ordinarysetwithowndescriptor
21:55
<Bakkot>
yeah, if the object is frozen
21:55
<Bakkot>
or prevent-extension'd
21:55
<Bakkot>
or sealed
21:55
<ljharb>
ok, and in those cases would you expect the current behavior where it silently avoids the issue?
21:56
<Bakkot>
what do you mean by silently avoids?
21:56
<ljharb>
i mean that `DefineOwnProperty` returns false when it fails
21:56
<ljharb>
so there's no abrupt completion for the ? to unwrap
21:56
<ljharb>
so, nothing happens
21:56
<ljharb>
ie the note on https://tc39.es/ecma262/#sec-createdataproperty
21:56
<ljharb>
(that's why CreateDataPropertyOrThrow exists)
22:02
<devsnek>
ljharb: it can still throw
22:03
<devsnek>
for example with proxies
22:04
<devsnek>
people even use `Object.defineProperty(); return true;` instead of `return Reflect.defineProperty()` because the former gives better error messages
22:06
<ljharb>
devsnek: sure but would OrdinarySetWithOwnDescriptor be called with a proxy?
22:07
<devsnek>
not sure about proxies
22:07
<devsnek>
but its used on lots of exotic objects
22:08
<ljharb>
right but the spec for OrdinarySetWithOwnDescriptor right now says that trying to add a property to a frozen/sealed/non-extensible object won't throw, if i'm reading it right
22:09
<devsnek>
for the objects defined in the spec i think that's true
22:10
<ljharb>
right but like, `Object.freeze(Array.prototype).foo = 3` throws in strict mode
22:11
<Bakkot>
if you have a proxy which doesn't handle some trap, it just uses the underlying object's trap
22:11
<Bakkot>
`(new Proxy({}, { get defineProperty(){ throw 'no'; } })).a = 0` throws `'no'` via OrdinarySetWithOwnDescriptor 3.d.iv
22:11
<Bakkot>
sorry, v3.e.ii
22:12
<Bakkot>
*via 3.e.ii
22:12
<Bakkot>
I am having difficulty with the typing today
22:12
<ljharb>
it throws in practice
22:12
<ljharb>
but in the spec text it does not throw
22:12
<Bakkot>
the thing I wrote throws in the spec
22:12
<ljharb>
oh wait
22:12
<ljharb>
right, because `O.[[DefineOwnProperty]]` throws
22:12
<ljharb>
ohhh i see
22:12
<ljharb>
so even on an ordinary object, it'd throw in the frozen etc case
22:12
<ljharb>
but the note on https://tc39.es/ecma262/#sec-createdataproperty says "If it does exist and is not configurable or if O is not extensible, [[DefineOwnProperty]] will return false."
22:13
<ljharb>
which isn't an exception
22:13
<ljharb>
so how does my above example throw in strict mode?
22:14
<Bakkot>
https://tc39.es/ecma262/#sec-putvalue
22:14
<Bakkot>
putvalue step 6.c
22:15
<devsnek>
speaking of strict references
22:15
<ljharb>
ok, so does that mean that `O.[[DefineOwnProperty]]`` won't ever return false in the normal object case (because it won't get there if it would otherwise have), but if a proxy returns false, it'd be silently ignored?
22:15
<ljharb>
(in the line i'm talking about in OrdinarySetWithOwnDescriptor, i mean)
22:16
<Bakkot>
it does get there?
22:16
<ljharb>
Bakkot: what invokes PutValue in the normal frozen object case?
22:16
<Bakkot>
assignmentexpression evaluation
22:16
<Bakkot>
https://tc39.es/ecma262/#sec-assignment-operators-runtime-semantics-evaluation
22:17
<ljharb>
ok, and PutValue invokes [[Set]]
22:17
<devsnek>
did the [[Set]] op used to be called [[Put]]
22:18
<ljharb>
Bakkot: but then how does assignment end up invoking OrdinarySetWithOwnDescriptor ?
22:18
<ljharb>
ah i see, nvm
22:18
<Bakkot>
devsnek yup
22:19
<Bakkot>
http://es5.github.io/#x8.12.5
22:19
<ljharb>
ok so then the "return `false`" is intentional there, because other code checks it and throws everywhere it happens to be used
22:19
<ljharb>
thanks for talking me through that