duktape icon indicating copy to clipboard operation
duktape copied to clipboard

aync / await

Open billiegoose opened this issue 7 years ago • 11 comments

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.

billiegoose avatar May 17 '17 16:05 billiegoose

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.

fatcerberus avatar May 17 '17 17:05 fatcerberus

@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.

svaarala avatar May 17 '17 18:05 svaarala

@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!

billiegoose avatar May 17 '17 19:05 billiegoose

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.

fatcerberus avatar May 17 '17 20:05 fatcerberus

@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 ;)

svaarala avatar May 17 '17 20:05 svaarala

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.

fatcerberus avatar May 17 '17 20:05 fatcerberus

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 😁

billiegoose avatar May 18 '17 00:05 billiegoose

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.

fatcerberus avatar May 18 '17 00:05 fatcerberus

@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).

fatcerberus avatar May 23 '17 17:05 fatcerberus

There's not much chatter here. Is async/await support planned for an upcoming version?

Type1J avatar May 27 '20 05:05 Type1J

@svaarala Any update?

mslijepc avatar Mar 29 '23 16:03 mslijepc