From 4a316bb7cac70666c7b64d5b5742c42966afce3e Mon Sep 17 00:00:00 2001 From: Kuba Orlik Date: Sun, 28 Mar 2021 08:34:45 +0200 Subject: [PATCH] Better DeadlyRace --- src/index.ts | 115 ++++++++++++++++++++++++++++++++------------------ tsconfig.json | 2 +- 2 files changed, 75 insertions(+), 42 deletions(-) diff --git a/src/index.ts b/src/index.ts index 16aec37..8020c9f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,17 +11,20 @@ class AbortableEmittery extends Emittery { } } -export default class AbortablePromise implements PromiseLike { +export default class AbortablePromise implements Promise { private emitter: AbortableEmittery; private result_promise: Promise; + private fulfilled = false; + private result: T; constructor( make_generator: ( await_or_abort: ( promise: Promise | AbortablePromise, on_abort?: () => Promise | void - ) => Promise + ) => Promise, + resolve?: Function ) => AsyncGenerator ) { const emitter = new AbortableEmittery(); @@ -31,6 +34,7 @@ export default class AbortablePromise implements PromiseLike { promise: Promise | AbortablePromise, on_abort?: () => Promise | void ): Promise { + console.log("await_or_abort", promise); if (promise instanceof AbortablePromise && !on_abort) { on_abort = async () => promise.abort(); } @@ -44,7 +48,7 @@ export default class AbortablePromise implements PromiseLike { emitter.on("abort", () => { if (!resolved) { (on_abort as Function)(); - reject(null); + resolve(null); resolved = true; } }); @@ -55,18 +59,15 @@ export default class AbortablePromise implements PromiseLike { resolved = true; } }) - .catch(reject); + .catch((err) => { + reject(err); + }); }); } this.result_promise = new Promise(async (resolve, reject) => { let step_value; const generator = make_generator(await_or_abort); - console.log("generator:", generator, make_generator); const e = new Error(); - if (!generator) { - console.error(e.stack); - console.log("generator:", generator); - } do { if (emitter.aborted) { resolve(null); @@ -75,6 +76,8 @@ export default class AbortablePromise implements PromiseLike { step_value = await generator.next(); } while (!step_value.done); resolve(step_value.value); + this.fulfilled = true; + this.result = step_value.value; }); this.emitter = emitter; } @@ -83,24 +86,6 @@ export default class AbortablePromise implements PromiseLike { this.emitter.abort(); } - static deadlyRace(promises: AbortablePromise[]): Promise { - 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( onfulfilled?: | ((value: T) => TResult1 | PromiseLike) @@ -110,9 +95,13 @@ export default class AbortablePromise implements PromiseLike { | ((reason: any) => TResult2 | PromiseLike) | undefined | null - ): PromiseLike { + ): AbortablePromise { const self = this; return new AbortablePromise(async function* (await_or_abort) { + if (self.fulfilled) { + onfulfilled(self.result); + return; + } const ret = await await_or_abort(self.result_promise, () => { self.abort(); }); @@ -122,6 +111,50 @@ export default class AbortablePromise implements PromiseLike { } }); } + + catch( + onrejected?: + | ((reason: any) => TResult | PromiseLike) + | undefined + | null + ): AbortablePromise { + 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( + promises: AbortablePromise[], + should_kill_others: (arg: L) => Promise = async () => true + ): Promise { + 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) => @@ -146,19 +179,19 @@ const sleep = promisify((timeout: number, callback: (...args: any[]) => void) => // console.log("awaited 300"); // }); -function abortableSleep(ms: number): AbortablePromise { - return new AbortablePromise(async function* () { - await sleep(ms); - yield; - console.log(`Slept ${ms}.`); - }); -} - -const b = AbortablePromise.deadlyRace([ - abortableSleep(1000), - abortableSleep(2000), - abortableSleep(3000), -]); +// function abortableSleep(ms: number): AbortablePromise { +// return new AbortablePromise(async function* () { +// await sleep(ms); +// yield; +// console.log(`Slept ${ms}.`); +// }); +// } + +// const b = AbortablePromise.deadlyRace([ +// abortableSleep(1000), +// abortableSleep(2000), +// abortableSleep(3000), +// ]); // setTimeout(() => a.abort(), 1500); diff --git a/tsconfig.json b/tsconfig.json index 8fa566d..41ac8e9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "noImplicitAny": true, "noImplicitThis": true, "strictNullChecks": true, - "target": "ES6", + "target": "ES2019", "declaration": true, "esModuleInterop": true, "lib": ["es6", "esnext"],