duktape
duktape copied to clipboard
aync / await
I've been thinking about it all morning, and I think that there's a way to map async
/ await
semantics onto new Duktape.Thread().resume()
/ Duktape.Thread.yield()
. They have similar error-throwing behavior, and don't have the weird .next()
interface that generators have.
You need one additional piece of puzzle compared to generators: Promise
. An async function returns a promise; await
basically just says, "when this promise is fulfilled, resume(me)
". So it's kind of like a yield and a (deferred) resume at the same time.
@wmhilton You're right, Duktape threads map closely to GeneratorFunctions. The plan forward is to see if Duktape Threads could be made a superset of GeneratorFunctions, and then figure out what to do with Promises, async, and await. Ideally as much as Duktape customness would go away, but still the free nature of Duktape coroutines would be preserved at least for code desiring that.
@fatcerberus Good catch - for some reason I thought Duktape had Promises already. If one calls an async
function without using await
then it does return a Promise. And I suppose that would be a highly desirable implementation detail, if only because Promises can be composed/nested elegantly. (Although I've had trouble composing the error handling elegantly.)
I've read the thread on generators... I'm still thinking it might be easier to implement the other way around: make Promises + async/await first class constructs, and then implement GeneratorFunctions's yield
using async/await. But I could be smoking crack! I need to do some more experimenting with Duktape.Thread first. I'm still extremely new to duktape.
By the way @svaarala ... how do you do it?! The incredible documentation, the number of features... this is such a huge project!
It's actually already possible to polyfill generator functions using Duktape threads, see: https://github.com/svaarala/duktape/issues/1101#issuecomment-264230874
There are a few minor differences in Duktape behavior compared to real ES generators that make this polyfill not 100%, but it comes pretty close.
@wmhilton Hmm, honestly I'd need to experiment a bit to be able to tell what approach works best. Working through GeneratorFunction would make sense from an incremental development point of view, but native Promise support (as opposed to using a library) is also on the wish list of many users so maybe Promises could come first.
By the way @svaarala ... how do you do it?! The incredible documentation, the number of features... this is such a huge project!
Thank you :) I'm just very stubborn ;)
I'm still thinking it might be easier to implement the other way around: make Promises + async/await first class constructs, and then implement GeneratorFunctions's yield using async/await.
That would be putting the cart before the horse IMO. async/await is described in many sources as being built on top of generators and Promise, so those are the lower-level constructs. async/await only seems like a lower-level abstraction because the syntax very deftly hides the underlying complexity.
async/await is described in many sources as being built on top of generators and Promise
Just because generators landed in ECMAScript first doesn't necessarily make them a prerequisite. The spec itself uses the phrase "directly building upon control flow structures parallel to those of generators, and using promises for the return type" which I interpret as Promises are absolutely necessary but not generators. Generators aren't ever mentioned again in the spec after that. :)
I know the current Babel implementation implements async/await by translating it into generator primitives, but I think the opposite - implement generators using async/await primitives - is possible. I'm not clear-headed enough to sketch out a proof yet... just more of a gut intuition.
async/await only seems like a lower-level abstraction because the syntax very deftly hides the underlying complexity.
Well, I will find out eventually 😁
I know the current Babel implementation implements async/await by translating it into generator primitives,
As does TypeScript (when using --downlevelIteration
, without it TS won't transpile them at all). IMO this seems like the more sane way to go about it, since await
conceptually does:
await some_promise;
// in other words:
// some_promise.then(resumeThisGenerator);
// yield;
This way of thinking about async/await also feels like a natural way to go about implementing it to me, but perhaps you're right after all. Like you say we'll find out eventually. :)
I guess when it comes down to it, both generators and async/await are special cases of coroutines. So in fact these are sibling concepts, rather than one being a subtype of the other.
I'll have to think about this some more myself, async/await is a feature I'd really love to see in Duktape, right up there with arrow functions.
@svaarala Thinking about this now, assuming a Promise
polyfill is provided, could one emulate await
by:
// await prom;
prom.then(Duktape.Thread.resume.bind(null, thisThread));
Duktape.Thread.yield();
Or would that cause unforeseen issues (i.e. resuming a thread from the job queue).
There's not much chatter here. Is async/await support planned for an upcoming version?
@svaarala Any update?