|
|
|
@ -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);
|
|
|
|
|
|
|
|
|
|