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, signal: AbortSignal,
promise: Promise<T>, promise: Promise<T>,
): Promise<T> { ): Promise<T> {
assertSignal(signal); return abortableFunc(signal, () => promise);
return Promise.race([ }
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) => { new Promise<T>((_, reject) => {
signal.addEventListener('abort', () => listener = () => {
reject(new DOMException('AbortError', 'AbortError')), 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 { BridgeMethods, methodNames } from './meta';
import workerURL from 'omt:../../../features-worker'; import workerURL from 'omt:../../../features-worker';
import type { ProcessorWorkerApi } from '../../../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. */ /** How long the worker should be idle before terminating. */
const workerTimeout = 10_000; const workerTimeout = 10_000;
@@ -40,29 +40,21 @@ for (const methodName of methodNames) {
this._queue = this._queue this._queue = this._queue
// Ignore any errors in the queue // Ignore any errors in the queue
.catch(() => {}) .catch(() => {})
.then(async () => { .then(() =>
if (signal.aborted) throw new DOMException('AbortError', 'AbortError'); abortableFunc(signal, async (setOnAbort) => {
clearTimeout(this._workerTimeout);
if (!this._worker) this._startWorker();
clearTimeout(this._workerTimeout); setOnAbort(() => this._terminateWorker());
if (!this._worker) this._startWorker();
const onAbort = () => this._terminateWorker(); return this._workerApi![methodName](...args).finally(() => {
signal.addEventListener('abort', onAbort); // Start a timer to clear up the worker.
this._workerTimeout = setTimeout(() => {
return abortable( this._terminateWorker();
signal, }, workerTimeout);
// @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);
// Start a timer to clear up the worker.
this._workerTimeout = setTimeout(() => {
this._terminateWorker();
}, workerTimeout);
});
});
return this._queue; return this._queue;
} as any; } as any;