export async function* eventToGenerator( cb: ( yieldCallback: (y: YieldType) => void, returnCallback: (r: ReturnType) => void, rejectCallack: (reason: unknown) => void ) => unknown ): AsyncGenerator { const promises: Array<{ p: Promise<{ done: true; value: ReturnType } | { done: false; value: YieldType }>; resolve: (value: { done: true; value: ReturnType } | { done: false; value: YieldType }) => void; reject: (reason?: unknown) => void; }> = []; function addPromise() { let resolve: (value: { done: true; value: ReturnType } | { done: false; value: YieldType }) => void; let reject: (reason?: unknown) => void; const p = new Promise<{ done: true; value: ReturnType } | { done: false; value: YieldType }>((res, rej) => { resolve = res; reject = rej; }); // @ts-expect-error TS doesn't know that promise callback is executed immediately promises.push({ p, resolve, reject }); } addPromise(); const callbackRes = Promise.resolve() .then(() => cb( (y) => { addPromise(); promises.at(-2)?.resolve({ done: false, value: y }); }, (r) => { addPromise(); promises.at(-2)?.resolve({ done: true, value: r }); }, (err) => promises.shift()?.reject(err) ) ) .catch((err) => promises.shift()?.reject(err)); while (1) { const p = promises[0]; if (!p) { throw new Error("Logic error in eventGenerator, promises should never be empty"); } const result = await p.p; promises.shift(); if (result.done) { await callbackRes; // Clean up, may be removed in the future // // Cleanup promises - shouldn't be needed due to above await // for (const promise of promises) { // promise.resolve(result); // await promise.p; // } return result.value; } yield result.value; } // So TS doesn't complain throw new Error("Unreachable"); }