File size: 1,895 Bytes
21dd449
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
export async function* eventToGenerator<YieldType, ReturnType>(
	cb: (
		yieldCallback: (y: YieldType) => void,
		returnCallback: (r: ReturnType) => void,
		rejectCallack: (reason: unknown) => void
	) => unknown
): AsyncGenerator<YieldType, ReturnType> {
	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");
}