// The drivers of async fns and generators // // The compiler lowers each async fn and generator to a coroutine (see // compiler/src/transform/async.rs): a step closure, which runs the body until // it suspends or returns, and an object `co`: // // { __proto__: , step, value, awaiting, result, done } // // A yield puts its value in `value`, an await puts its promise in `awaiting`, // and a return puts its value in `result` and sets `done`. The fn returns // `co.start()`. Only these drivers differ between the kinds // // Not supported yet: `throw()` on a generator, and `finally` blocks in // `return()`, which need exceptions import { __is_promise, __reject, __resolve } from "jsrt:promise"; // A generator's object is `co` itself export const __GeneratorProto = { // The body runs up to its `initial_yield`, after its params start() { this.step(undefined); return this; }, next(input) { if (this.done) return { value: undefined, done: true }; this.step(input); if (this.done) return { value: this.result, done: true }; return { value: this.value, done: false }; }, return(value) { this.done = true; return { value: value, done: true }; }, }; // An async fn returns a promise, which its return fulfils. A rejected await // rejects it, and the rest of the body does not run, as an uncaught throw at the // await would export const __AsyncProto = { // The body runs up to its first await, in the call start() { const promise = new Promise(undefined); this.run(promise); return promise; }, run(promise) { this.step(undefined); if (this.done) __resolve(promise, this.result); else this.awaiting.then(() => this.run(promise), (reason) => __reject(promise, reason)); }, // What an await waits on: a promise as it is, any other value as a fulfilled one. // The step reads the value from it when it resumes toPromise(x) { return __is_promise(x) ? x : Promise.resolve(x); }, };