News for hackers. Stuff that matters.
Re:Why Would Anyone Need JavaScript Generator Functions? by jitl on Monday November 07, 2022 @10:24AM (#33506965)
You can use One Weird Trick with generator functions to make your code "generic" over synchronicity. I use this technique to avoid needing to implement both sync and async versions of some functions in my quickjs-emscripten library.

The great part about this technique as a library author is that unlike choosing to use a Promise return type, this technique is invisible in my public API. I can write a function like `export function coolAlgorithm(getData: (request: I) => O | Promise<O>): R | Promise<R>`, and we get automatic performance improvement if the caller's `getData` function happens to return synchronously, without mystery generator stuff showing up in the function signature.

Helper to make a function that can be either sync or async: https://github.com/justjake/quickjs-emscripten/blob/ff211447...

Uses: https://cs.github.com/justjake/quickjs-emscripten?q=yield*+l...

Re:Why Would Anyone Need JavaScript Generator Functions? by athanagor2 on Monday November 07, 2022 @10:45AM (#33507301)
I used a very similar pattern in a web app, in which the user creates objects by clicking in different places. There is a state built by each click, and it is convenient to just go back one or several steps behind sometimes. So you have something like

  const input1 = yield
  // arbitrary side effects, UI updates etc.
  const input2 = yield
  // ...
Each time the user makes an input, it is accumulated in a list, and sent back to the generator function. When the user decides to cancel the last step, a generator is recreated and rerun, with each yield sending back the user input that was stored in the list, except the last one. This requires writing the generator function in a particular way (you have to avoid setting "external" state), but it works and is more flexible than automata, I think.
Re:Why Would Anyone Need JavaScript Generator Functions? by jupp0r on Monday November 07, 2022 @12:04PM (#33508615)
The problem with this is that it breaks some tooling (async stack traces in Chrome dev tools for example).
Re:Why Would Anyone Need JavaScript Generator Functions? by c-baby on Monday November 07, 2022 @02:40PM (#33511218)
This seems a lot more complicated than something like this:

    if (!(value instanceof Promise)) {
        value = Promise.resolve(value)
    }
And then you write the rest of your code as if it is async.
Re:Why Would Anyone Need JavaScript Generator Functions? by tobr on Monday November 07, 2022 @04:01PM (#33512392)
Not as if. You’re making a synchronous call async. And so it keeps spreading through your code. Op’s trick is certainly more complicated but looks like a smart way to support async cases but still letting synchronous calls stay synchronous.
Re:Why Would Anyone Need JavaScript Generator Functions? by c-baby on Monday November 07, 2022 @08:00PM (#33514721)
Hmm I see. So the value is in creating a kind of blocking promise. I can see how that might be useful in certain circumstances, but using blocking functionality isn't something you'd want to rely on in general.
Re:Why Would Anyone Need JavaScript Generator Functions? by qazxcvbnm on Tuesday November 08, 2022 @12:10AM (#33516057)
No, the promise doesn't block, it just stays a promise. However, for synchronous calls, the Promise overhead is obviated.

In other words, the performance price of synchronous vs asynchronous calls is the price of a function call vs Promise implementation (i.e. event-loop machinery); the price of a function call, including the stack frame allocation, which is non-zero (recall the times that assembly programmers would dismiss languages like C as 'too-slow', having to allocate on function calls), versus pushing the Promise closure onto an event-loop, exiting the current event-loop, waiting for the next event-tick, popping off the next closure from the event stack, then creating the function call stack frame under a closure.

For inner-loops, the difference can be 20ms vs 20s.

Re:Why Would Anyone Need JavaScript Generator Functions? by pmoleri on Monday November 07, 2022 @10:11PM (#33515494)
I don't get it, the signature still returns `Type | Promise<Type>` how that promise doesn't spread through your code?

Still, the other comment about using await on plain values seems like a better option than parent comment

Re:Why Would Anyone Need JavaScript Generator Functions? by tobr on Tuesday November 08, 2022 @01:34AM (#33516478)
c-baby’s suggestion wraps a non-promise in a promise, so I don’t see how that’s still able to return a non-promise.

Awaiting plain values can only be done inside an async function, which means it returns a promise, which means you have to wait for the event loop to get the value out of there.

Generally I’m not aware of any other (reasonably ergonomic) way to write a single code path that can work with both sync and async input without itself always giving async output.

Re:Why Would Anyone Need JavaScript Generator Functions? by jitl on Monday November 07, 2022 @04:12PM (#33512530)
It's very easy to make sync code async. Just add an `async` to the function declaration. You don't need the `instanceof` check or `Promise.resolve` - you can `await` any type of value; it'll get unwrapped as a Promise if it has a `then` method, or otherwise just give you back the value. See [MDN for await].

If that's okay for your code, then go for it. However, downgrading a sync function to be async has serious performance implications - turning a trivial sync function call in a tight loop into an awaited async function call will make your loop 90%+ slower [benchmark]. You're also going to allocate much more memory for Promise objects. The code I linked above is from a library implementing a sandboxed Javascript VM. If we forced all calls into the guest sandbox, or from the guest sandbox back to host functions like `console.log`, to be async, the VM would be unusable for many applications.

[MDN for await]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

[benchmark]: https://jsbench.me/y0la7auape/2

[dead]
Re:Why Would Anyone Need JavaScript Generator Functions? by qazxcvbnm on Tuesday November 08, 2022 @12:20AM (#33516115)
OP's technique is useful for a consumer of a another function, to consume it in a synchronity-agnostic manner. In the implementation of the function consumer, you should find the usage of OP's technique.

In other words, this technique allows a library's implementation and interface to be synchronity-agnostic, but it does not say anything about the library user. If the library user likewise makes use of the OP technique, the library user code will remain synchronity-agnostic, otherwise it will be tied to be either synchronous or be asynchronous (parametrised to the synchronity).

Re:Why Would Anyone Need JavaScript Generator Functions? by bakkoting on Monday November 07, 2022 @06:09PM (#33513835)
Babel does this, using a library called gensync. I have a writeup which goes into more detail: https://writing.bakkot.com/gensync

gensync instead creates two functions, one sync and one async, which is probably a more familiar API for consumers of your code.