Compare commits

...

3 Commits

Author SHA1 Message Date
1c1e4c7c01 Better types 2021-02-05 14:36:36 +00:00
73ac08c0cd Don't need async here 2021-02-05 12:23:17 +00:00
7ff637a1ff Trying out abortableFunc 2021-02-05 12:11:33 +00:00
2 changed files with 49 additions and 30 deletions

View File

@ -380,19 +380,46 @@ export function assertSignal(signal: AbortSignal) {
* Take a signal and promise, and returns a promise that rejects with an AbortError if the abort is
* signalled, otherwise resolves with the promise.
*/
export async function abortable<T>(
export function abortable<T>(
signal: AbortSignal,
promise: Promise<T>,
): Promise<T> {
assertSignal(signal);
return Promise.race([
promise,
return abortableFunc(signal, () => promise);
}
type SetAbortArg = (() => void) | undefined;
type AbortableCallback<T> = (
setAbort: (abortCallback: SetAbortArg) => 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) | undefined;
let listener: () => void;
const setOnAbort = (abortCallback: SetAbortArg) => {
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();
clearTimeout(this._workerTimeout);
if (!this._worker) this._startWorker();
setOnAbort(() => this._terminateWorker());
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);
// Start a timer to clear up the worker.
this._workerTimeout = setTimeout(() => {
this._terminateWorker();
}, workerTimeout);
});
});
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;