working deadlyrace

master
Kuba Orlik 4 years ago
parent ee3d998c08
commit f3568c7641

@ -1,4 +1,5 @@
import Emittery from "emittery"; import Emittery from "emittery";
import { promisify } from "util";
//needed to pass information on wether or not the promise has been aborted witout 'this' //needed to pass information on wether or not the promise has been aborted witout 'this'
class AbortableEmittery extends Emittery { class AbortableEmittery extends Emittery {
@ -10,33 +11,45 @@ class AbortableEmittery extends Emittery {
} }
} }
export default class AbortablePromise<T> extends Promise<T | null> { export default class AbortablePromise<T> implements PromiseLike<T> {
private emitter: AbortableEmittery; private emitter: AbortableEmittery;
private result_promise: Promise<T>;
constructor( constructor(
make_generator: ( make_generator: (
await_or_abort: ( await_or_abort: <L>(
promise: Promise<any>, promise: Promise<L> | AbortablePromise<L>,
on_abort: () => Promise<unknown> on_abort?: () => Promise<unknown> | void
) => Promise<unknown> ) => Promise<L>
) => AsyncGenerator ) => AsyncGenerator
) { ) {
const emitter = new AbortableEmittery(); const emitter = new AbortableEmittery();
const await_or_abort = (
promise: Promise<any>, function await_or_abort<L = any>(promise: AbortablePromise<L>): Promise<L>;
on_abort: () => Promise<unknown> function await_or_abort<L = any>(
) => { promise: Promise<L> | AbortablePromise<L>,
on_abort?: () => Promise<unknown> | void
): Promise<L> {
if (promise instanceof AbortablePromise && !on_abort) {
on_abort = async () => promise.abort();
}
if (on_abort === undefined) {
throw new Error(
"on_abort is required when the first argument is not AbortablePromise"
);
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let resolved = false; let resolved = false;
emitter.on("abort", () => { emitter.on("abort", () => {
if (!resolved) { if (!resolved) {
on_abort(); (on_abort as Function)();
resolve(null); reject(null);
resolved = true; resolved = true;
} }
}); });
promise promise
.then((result) => { .then((result: L) => {
if (!resolved) { if (!resolved) {
resolve(result); resolve(result);
resolved = true; resolved = true;
@ -44,10 +57,16 @@ export default class AbortablePromise<T> extends Promise<T | null> {
}) })
.catch(reject); .catch(reject);
}); });
}; }
super(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();
if (!generator) {
console.error(e.stack);
console.log("generator:", generator);
}
do { do {
if (emitter.aborted) { if (emitter.aborted) {
resolve(null); resolve(null);
@ -63,21 +82,84 @@ export default class AbortablePromise<T> extends Promise<T | null> {
abort() { abort() {
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>(
onfulfilled?:
| ((value: T) => TResult1 | PromiseLike<TResult1>)
| undefined
| null,
onrejected?:
| ((reason: any) => TResult2 | PromiseLike<TResult2>)
| undefined
| null
): PromiseLike<TResult1 | TResult2> {
const self = this;
return new AbortablePromise(async function* (await_or_abort) {
const ret = await await_or_abort(self.result_promise, () => {
self.abort();
});
yield;
if (onfulfilled) {
onfulfilled(ret);
}
});
}
} }
// const sleep = promisify((timeout: number, callback: (...args: any[]) => void) => const sleep = promisify((timeout: number, callback: (...args: any[]) => void) =>
// setTimeout(callback, timeout) setTimeout(callback, timeout)
// ); );
// const a = new AbortablePromise(async function* () { // const a = new AbortablePromise(async function* () {
// yield await sleep(1000); // yield await sleep(1000);
// console.log("awaited 100"); // console.log("awaited 100");
// yield await sleep(2000); // yield await sleep(2000);
// console.log("awaited 200"); // console.log("awaited 200");
// yield await sleep(3000); // yield await sleep(3000);
// console.log("awaited 300"); // console.log("awaited 300");
// });
//
// const a = new AbortablePromise(async function* () {
// yield await sleep(1000);
// console.log("awaited 100");
// yield await sleep(2000);
// console.log("awaited 200");
// yield await sleep(3000);
// 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),
]);
// setTimeout(() => a.abort(), 1500); // setTimeout(() => a.abort(), 1500);
// const b = new AbortablePromise(async function* (await_or_abort) { // const b = new AbortablePromise(async function* (await_or_abort) {

Loading…
Cancel
Save