Better DeadlyRace

master
Kuba Orlik 4 years ago
parent f3568c7641
commit 4a316bb7ca

@ -11,17 +11,20 @@ class AbortableEmittery extends Emittery {
} }
} }
export default class AbortablePromise<T> implements PromiseLike<T> { export default class AbortablePromise<T> implements Promise<T | null> {
private emitter: AbortableEmittery; private emitter: AbortableEmittery;
private result_promise: Promise<T>; private result_promise: Promise<T>;
private fulfilled = false;
private result: T;
constructor( constructor(
make_generator: ( make_generator: (
await_or_abort: <L>( await_or_abort: <L>(
promise: Promise<L> | AbortablePromise<L>, promise: Promise<L> | AbortablePromise<L>,
on_abort?: () => Promise<unknown> | void on_abort?: () => Promise<unknown> | void
) => Promise<L> ) => Promise<L>,
resolve?: Function
) => AsyncGenerator ) => AsyncGenerator
) { ) {
const emitter = new AbortableEmittery(); const emitter = new AbortableEmittery();
@ -31,6 +34,7 @@ export default class AbortablePromise<T> implements PromiseLike<T> {
promise: Promise<L> | AbortablePromise<L>, promise: Promise<L> | AbortablePromise<L>,
on_abort?: () => Promise<unknown> | void on_abort?: () => Promise<unknown> | void
): Promise<L> { ): Promise<L> {
console.log("await_or_abort", promise);
if (promise instanceof AbortablePromise && !on_abort) { if (promise instanceof AbortablePromise && !on_abort) {
on_abort = async () => promise.abort(); on_abort = async () => promise.abort();
} }
@ -44,7 +48,7 @@ export default class AbortablePromise<T> implements PromiseLike<T> {
emitter.on("abort", () => { emitter.on("abort", () => {
if (!resolved) { if (!resolved) {
(on_abort as Function)(); (on_abort as Function)();
reject(null); resolve(null);
resolved = true; resolved = true;
} }
}); });
@ -55,18 +59,15 @@ export default class AbortablePromise<T> implements PromiseLike<T> {
resolved = true; resolved = true;
} }
}) })
.catch(reject); .catch((err) => {
reject(err);
});
}); });
} }
this.result_promise = new Promise(async (resolve, reject) => { this.result_promise = new Promise(async (resolve, reject) => {
let step_value; let step_value;
const generator = make_generator(await_or_abort); const generator = make_generator(await_or_abort);
console.log("generator:", generator, make_generator);
const e = new Error(); const e = new Error();
if (!generator) {
console.error(e.stack);
console.log("generator:", generator);
}
do { do {
if (emitter.aborted) { if (emitter.aborted) {
resolve(null); resolve(null);
@ -75,6 +76,8 @@ export default class AbortablePromise<T> implements PromiseLike<T> {
step_value = await generator.next(); step_value = await generator.next();
} while (!step_value.done); } while (!step_value.done);
resolve(step_value.value); resolve(step_value.value);
this.fulfilled = true;
this.result = step_value.value;
}); });
this.emitter = emitter; this.emitter = emitter;
} }
@ -83,24 +86,6 @@ export default class AbortablePromise<T> implements PromiseLike<T> {
this.emitter.abort(); this.emitter.abort();
} }
static deadlyRace<L>(promises: AbortablePromise<L>[]): Promise<L> {
return new Promise((resolve) => {
let resolved = false;
const callback = (arg: L) => {
if (resolved) return;
resolved = true;
for (const promise of promises) {
console.log("one of the promises resolved! killing the others");
promise.abort();
}
resolve(arg);
};
for (const promise of promises) {
promise.then(callback);
}
});
}
then<TResult1 = T, TResult2 = never>( then<TResult1 = T, TResult2 = never>(
onfulfilled?: onfulfilled?:
| ((value: T) => TResult1 | PromiseLike<TResult1>) | ((value: T) => TResult1 | PromiseLike<TResult1>)
@ -110,9 +95,13 @@ export default class AbortablePromise<T> implements PromiseLike<T> {
| ((reason: any) => TResult2 | PromiseLike<TResult2>) | ((reason: any) => TResult2 | PromiseLike<TResult2>)
| undefined | undefined
| null | null
): PromiseLike<TResult1 | TResult2> { ): AbortablePromise<TResult1 | TResult2> {
const self = this; const self = this;
return new AbortablePromise(async function* (await_or_abort) { return new AbortablePromise(async function* (await_or_abort) {
if (self.fulfilled) {
onfulfilled(self.result);
return;
}
const ret = await await_or_abort(self.result_promise, () => { const ret = await await_or_abort(self.result_promise, () => {
self.abort(); self.abort();
}); });
@ -122,6 +111,50 @@ export default class AbortablePromise<T> implements PromiseLike<T> {
} }
}); });
} }
catch<TResult = never>(
onrejected?:
| ((reason: any) => TResult | PromiseLike<TResult>)
| undefined
| null
): AbortablePromise<T | TResult> {
const self = this;
return new AbortablePromise(async function* (await_or_abort) {
await await_or_abort(self.result_promise, () => {
self.abort();
}).catch(onrejected);
yield;
});
}
static deadlyRace<L>(
promises: AbortablePromise<L>[],
should_kill_others: (arg: L) => Promise<boolean> = async () => true
): Promise<L> {
return new AbortablePromise(async function* (await_or_abort) {
return await_or_abort(
new Promise((resolve, reject) => {
let resolved = false;
const callback = async (arg: L) => {
if (resolved) return;
if (arg === null) return;
resolved = await should_kill_others(arg);
if (!resolved) return;
for (const promise of promises) {
promise.abort();
}
resolve(arg);
};
for (const promise of promises) {
promise.then(callback);
}
}),
() => {
promises.forEach((promise) => promise.abort());
}
);
});
}
} }
const sleep = promisify((timeout: number, callback: (...args: any[]) => void) => const sleep = promisify((timeout: number, callback: (...args: any[]) => void) =>
@ -146,19 +179,19 @@ const sleep = promisify((timeout: number, callback: (...args: any[]) => void) =>
// console.log("awaited 300"); // console.log("awaited 300");
// }); // });
function abortableSleep(ms: number): AbortablePromise<void> { // function abortableSleep(ms: number): AbortablePromise<void> {
return new AbortablePromise(async function* () { // return new AbortablePromise(async function* () {
await sleep(ms); // await sleep(ms);
yield; // yield;
console.log(`Slept ${ms}.`); // console.log(`Slept ${ms}.`);
}); // });
} // }
const b = AbortablePromise.deadlyRace([ // const b = AbortablePromise.deadlyRace([
abortableSleep(1000), // abortableSleep(1000),
abortableSleep(2000), // abortableSleep(2000),
abortableSleep(3000), // abortableSleep(3000),
]); // ]);
// setTimeout(() => a.abort(), 1500); // setTimeout(() => a.abort(), 1500);

@ -5,7 +5,7 @@
"noImplicitAny": true, "noImplicitAny": true,
"noImplicitThis": true, "noImplicitThis": true,
"strictNullChecks": true, "strictNullChecks": true,
"target": "ES6", "target": "ES2019",
"declaration": true, "declaration": true,
"esModuleInterop": true, "esModuleInterop": true,
"lib": ["es6", "esnext"], "lib": ["es6", "esnext"],

Loading…
Cancel
Save