@legumeinfo/web-components
Version:
Web Components for the Legume Information System and other AgBio databases
111 lines • 3.87 kB
JavaScript
/**
* A controller that allows Promises to be cancelled.
*
* Note that all Promises made cancellable with this controller are cancelled
* with the same AbortSignal. Multiple instances of the controller should be
* used if multiple signals are desired.
*/
export class LisCancelPromiseController {
/**
* @param host - The component that's using the controller.
*/
constructor(host) {
/** @ignore */
this._listeners = [];
(this.host = host).addController(this);
// members ignored because they're not definitely assigned in the constructor
this._initialize();
}
/** @ignore */
hostConnected() {
this._addEventListener();
}
/** @ignore */
hostDisconnected() {
this.abortSignal.removeEventListener('abort', this._aborted.bind(this));
}
/**
* Makes a Promise cancellable by racing it against a Promise that only
* cancels.
*
* @typeParam T - The type of the value the {@link !Promise | `Promise`} to be
* wrapped resolves to.
*
* @param promise - The promise to wrap.
*
* @returns A new {@link !Promise | `Promise`} that will resolve if the
* `promise` parameter resolves or reject with the `'abort'`
* {@link !Event | `Event`} raised by the
* {@link LisCancelPromiseController.abortSignal | `abortSignal`} if the
* promise is cancelled.
*/
wrapPromise(promise) {
// create a Promise race that will reject when the abort signal emits
const cancelState = this._cancelState;
cancelState.wrapCount += 1;
return Promise.race([promise, cancelState.promise]);
}
/**
* Adds a listener to the abort event.
*
* @param listener - The listener to subscribe to cancel events.
*/
addListener(listener) {
// each listener is called in the scope of the host
this._listeners.push(listener.bind(this.host));
}
/**
* Cancels all Promises that have been made since the last cancel.
*/
cancel() {
this._abortController.abort();
}
/** @ignore */
_addEventListener() {
this.abortSignal.addEventListener('abort', this._aborted.bind(this), {
once: true,
});
}
/** @ignore */
// creates the Promise that only cancels
_initialize() {
// initialize the AbortController and the AbortSignal variable
this._abortController = new AbortController();
this.abortSignal = this._abortController.signal;
// add the abort event listener
this._addEventListener();
// intialize the cancel state
const cancelState = {
abortSignal: this.abortSignal,
wrapCount: 0,
};
cancelState.promise = new Promise((_, reject) => {
// cancel the promise when the abort signal emits
cancelState.abortSignal.addEventListener('abort', (event) => reject(event), { once: true });
})
// the default error handler
.catch((error) => {
// only throw an error if a Promise downstream can catch it
if (cancelState.wrapCount > 0) {
throw error;
}
});
this._cancelState = cancelState;
}
/** @ignore */
// calls all listeners of the aobrt event
_aborted(event) {
// create a new cancel only promise
this._initialize();
// redraw the host
this.host.requestUpdate();
// wait for the redraw to complete in case any listeners rely on state from the template
this.host.updateComplete.then(() => {
// call each listener
this._listeners.forEach((listener) => {
listener(event);
});
});
}
}
//# sourceMappingURL=lis-cancel-promise-controller.js.map