18:38
<ptomato>
I'm looking into the Array.fromAsync proposal and have a question about what people's understanding of "Zalgo" is (to explain the jargon, Zalgo is a reference to a meme, that is used as shorthand for the situation where an async operation sometimes calls a callback and sometimes doesn't)
18:39
<ptomato>
is throwing an error immediately, on bad input, Zalgo?
18:40
<ptomato>

i.e.

function myfunc(input) {
  if (isBad(input)) throw new Error('bad!');
  return promiseFromAsyncOperation(input);
}
18:41
<ptomato>
the Array.fromAsync proposal says yes and takes care to check its input only inside its async closure, which means rejecting the promise, not throwing an error synchronously
18:41
<ptomato>
Atomics.waitAsync, on the other hand, does throw errors synchronously on bad input: (steps 1 and 2 of https://tc39.es/proposal-atomics-wait-async/#sec-dowait)
18:43
<ptomato>
reading the original definitions of Zalgo, I'd say this is not Zalgo as originally defined
18:44
<ptomato>
however, maybe it is still desirable to do one or the other; in which case we should probably adjust one of these two proposals
18:45
<Kris Kowal>
Zalgo in short is a combinatoric explosion of behaviors that may be either synchronous or anachronous, such that it is not practical to test every combination that will be seen in production.
18:46
<Kris Kowal>
Vetting inputs is I believe debatable. It depends on whether the errant behavior can possibly vary at runtime.
18:47
<Kris Kowal>
Being JavaScript, it almost certainly can.
18:47
<ljharb>
the language has explicit decided with async function that sync exceptions should never be thrown from something that returns a promise. there's a few exceptions, i think, if you're calling something on the wrong receiver, but generally we should avoid that
18:47
<bakkot>
ptomato: I would say that Atomics.waitAsync is actually a sync function, despite the name
18:47
<bakkot>
it does not return a promise; it returns an object which is synchronously usable
18:47
<ljharb>
can it return a promise?
18:47
<ljharb>
ah ok
18:47
<ljharb>
then i would agree
18:47
<bakkot>
(one of the properties of that object can be a promise)
18:47
<shu>
Atomics.waitAsync is the way it is explicitly to avoid zalgo
18:47
<ljharb>
zalgo is basically "always, or never, return a promise"
18:48
<ljharb>
"an object containing a promise" is definitely not a promise :-)
18:48
<shu>
in particular, Atomics.waitAsync needs to fail fast because it's built to mutexes -- you can't wait until the next microtask tick to find out you couldn't acquire the lock and need to go into the slow path
18:49
<Kris Kowal>
the language has explicit decided with async function that sync exceptions should never be thrown from something that returns a promise. there's a few exceptions, i think, if you're calling something on the wrong receiver, but generally we should avoid that
And also the Promise constructor.
18:49
<shu>
so, instead of always returning a Promise it does this wrapping
18:49
<ptomato>
fair enough. I got misled by the first sentence of MDN on Atomics.waitAsync: "The static Atomics.waitAsync() method waits asynchronously on a shared memory location and returns a Promise."
18:49
<shu>
it waits asynchronously if it waits at all
18:49
<ptomato>
reading further down, you are right that it actually does not return a Promise
18:49
<shu>
if it doesn't wait, you can find out synchronously
18:52
<ptomato>
the language has explicit decided with async function that sync exceptions should never be thrown from something that returns a promise. there's a few exceptions, i think, if you're calling something on the wrong receiver, but generally we should avoid that
is there documentation for this decision? seems like a good thing to put in https://github.com/tc39/how-we-work/pull/119 once that document is established
18:52
<shu>
all that said i love zalgo
18:52
<shu>
i wish we had zalgo
18:52
<shu>
but alas
19:02
<ptomato>
well, in lieu of spec conventions, if anyone has a pointer to context about that decision, I'd be happy to read more. maybe I'll write a blog post on this or something
19:03
<joepie91 ๐Ÿณ๏ธโ€๐ŸŒˆ>
ptomato: https://blog.izs.me/2013/08/designing-apis-for-asynchrony/ is pretty much the canonical source on this topic
19:03
<ptomato>
thanks, but I linked that document above already ๐Ÿ˜„
19:04
<joepie91 ๐Ÿณ๏ธโ€๐ŸŒˆ>
oh, sorry.
19:04
<joepie91 ๐Ÿณ๏ธโ€๐ŸŒˆ>
only half paying attention today
19:04
<ptomato>
sync exceptions are not Zalgo according to that definition, so that's why I'm interested in the context of where we decided to depart from that
19:05
<joepie91 ๐Ÿณ๏ธโ€๐ŸŒˆ>
I'm unsure of the exact process behind that decision, but it does meet the principles behind zalgo; errors that are sometimes synchronous and sometimes asynchronous make it difficult to reason about the behaviour of a function
19:09
<ptomato>
I tend to agree, although the article (and the one from Havoc Pennington before it) concentrates, rightly IMO, on the situation where a callback is called sometimes synchronously and sometimes asynchronously, which is much, much worse
19:10
<Ashley Claymore>
I found it more of an issue when .then was more common. Would frequently see code like .catch(handleError) but sometimes the code would sync throw and that wasnโ€™t handled.
Now await is here itโ€™s less common, and code uses a catch block for both paths. But still seems like a good principle to follow
19:11
<ptomato>
it's effectively a slightly different question; is it OK for the callback to not be called at all? (and I think there is good reason to answer no)
19:13
<ptomato>
which is apparently also what TC39 has concluded as well
19:17
<Ashley Claymore>
Many people, myself included, use zalgo also for when an api always calls the callback, but can either get the call before the outer call returns (current tick) or after (fresh tick)
19:19
<Ashley Claymore>
I think it is ok to further expand that to: when will the error information be available
19:19
<Ashley Claymore>
cuts down code paths if always async
19:20
<ptomato>
if the call happens before the outer call returns, isn't that effectively calling it synchronously?
19:25
<Ashley Claymore>
yeah
19:28
<Ashley Claymore>
ah right, and a sync error would be โ€œnot calling the callback at allโ€. Which yes is annoying. If the API returns a Result like container, then it has somewhere to return errors. It something is sync returning a value directly, it kinda has to throw if it wants to โ€œreturnโ€ an error