Better DeadlyRace

master
Kuba Orlik 3 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 result_promise: Promise<T>;
private fulfilled = false;
private result: T;
constructor(
make_generator: (
await_or_abort: <L>(
promise: Promise<L> | AbortablePromise<L>,
on_abort?: () => Promise<unknown> | void
) => Promise<L>
) => Promise<L>,
resolve?: Function
) => AsyncGenerator
) {
const emitter = new AbortableEmittery();
@ -31,6 +34,7 @@ export default class AbortablePromise<T> implements PromiseLike<T> {
promise: Promise<L> | AbortablePromise<L>,
on_abort?: () => Promise<unknown> | void
): Promise<L> {
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<T> implements PromiseLike<T> {
emitter.on("abort", () => {
if (!resolved) {
(on_abort as Function)();
reject(null);
resolve(null);
resolved = true;
}
});
@ -55,18 +59,15 @@ export default class AbortablePromise<T> implements PromiseLike<T> {
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<T> implements PromiseLike<T> {
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<T> implements PromiseLike<T> {
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>(
onfulfilled?:
| ((value: T) => TResult1 | PromiseLike<TResult1>)
@ -110,9 +95,13 @@ export default class AbortablePromise<T> implements PromiseLike<T> {
| ((reason: any) => TResult2 | PromiseLike<TResult2>)
| undefined
| null
): PromiseLike<TResult1 | TResult2> {
): AbortablePromise<TResult1 | TResult2> {
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<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) =>
@ -146,19 +179,19 @@ const sleep = promisify((timeout: number, callback: (...args: any[]) => void) =>
// console.log("awaited 300");
// });
function abortableSleep(ms: number): AbortablePromise<void> {
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<void> {
// 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);

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

Loading…
Cancel
Save