Trying out abortableFunc

This commit is contained in:
Jake Archibald
2021-02-05 12:11:33 +00:00
parent dc40f84a65
commit 7ff637a1ff
2 changed files with 47 additions and 29 deletions

View File

@@ -384,15 +384,41 @@ export async function abortable<T>(
signal: AbortSignal,
promise: Promise<T>,
): Promise<T> {
assertSignal(signal);
return Promise.race([
promise,
return abortableFunc(signal, () => promise);
}
type AbortableCallback<T> = (
setAbort: (abortCallback: () => void) => void,
) => Promise<T>;
/**
* A helper to create abortable things.
*
* @param signal Signal to abort the task
* @param callback The task
*/
export async function abortableFunc<T>(
signal: AbortSignal | undefined,
callback: AbortableCallback<T>,
): Promise<T> {
if (signal) assertSignal(signal);
let onAbort: () => void;
let listener: () => void;
const setOnAbort = (abortCallback: () => void) => {
onAbort = abortCallback;
};
const promise = callback(setOnAbort);
return Promise.race<T>([
new Promise<T>((_, reject) => {
signal.addEventListener('abort', () =>
reject(new DOMException('AbortError', 'AbortError')),
);
listener = () => {
onAbort?.();
reject(new DOMException('AbortError', 'AbortError'));
};
signal?.addEventListener('abort', listener);
}),
]);
promise,
]).finally(() => signal?.removeEventListener('abort', listener));
}
/**

View File

@@ -2,7 +2,7 @@ import { wrap } from 'comlink';
import { BridgeMethods, methodNames } from './meta';
import workerURL from 'omt:../../../features-worker';
import type { ProcessorWorkerApi } from '../../../features-worker';
import { abortable } from '../util';
import { abortableFunc } from '../util';
/** How long the worker should be idle before terminating. */
const workerTimeout = 10_000;
@@ -40,29 +40,21 @@ for (const methodName of methodNames) {
this._queue = this._queue
// Ignore any errors in the queue
.catch(() => {})
.then(async () => {
if (signal.aborted) throw new DOMException('AbortError', 'AbortError');
.then(() =>
abortableFunc(signal, async (setOnAbort) => {
clearTimeout(this._workerTimeout);
if (!this._worker) this._startWorker();
const onAbort = () => this._terminateWorker();
signal.addEventListener('abort', onAbort);
return abortable(
signal,
// @ts-ignore - TypeScript can't figure this out
this._workerApi![methodName](...args),
).finally(() => {
// No longer care about aborting - this task is complete.
signal.removeEventListener('abort', onAbort);
setOnAbort(() => this._terminateWorker());
return this._workerApi![methodName](...args).finally(() => {
// Start a timer to clear up the worker.
this._workerTimeout = setTimeout(() => {
this._terminateWorker();
}, workerTimeout);
});
});
}),
);
return this._queue;
} as any;