|
|
import { CancellationAggregate } from "./CancellationAggregate";
|
|
|
import { ICancellation, IDestroyable } from "./interfaces";
|
|
|
import { argumentNotNull, destroyed } from "./safe";
|
|
|
|
|
|
export class Cancellation implements ICancellation {
|
|
|
private _reason: any;
|
|
|
private _cbs: Array<(e: any) => void> | undefined;
|
|
|
|
|
|
constructor(action: (cancel: (e?: any) => void) => void) {
|
|
|
argumentNotNull(action, "action");
|
|
|
|
|
|
action(this._cancel.bind(this));
|
|
|
}
|
|
|
|
|
|
isSupported(): boolean {
|
|
|
return true;
|
|
|
}
|
|
|
throwIfRequested(): void {
|
|
|
if (this._reason)
|
|
|
throw this._reason;
|
|
|
}
|
|
|
|
|
|
isRequested(): boolean {
|
|
|
return !!this._reason;
|
|
|
}
|
|
|
|
|
|
register(cb: (e: any) => void): IDestroyable {
|
|
|
argumentNotNull(cb, "cb");
|
|
|
|
|
|
if (this._reason) {
|
|
|
cb(this._reason);
|
|
|
return destroyed;
|
|
|
} else {
|
|
|
if (!this._cbs)
|
|
|
this._cbs = [cb];
|
|
|
else
|
|
|
this._cbs.push(cb);
|
|
|
|
|
|
const me = this;
|
|
|
return {
|
|
|
destroy() {
|
|
|
me._unregister(cb);
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private _unregister(cb: any) {
|
|
|
if (this._cbs) {
|
|
|
const i = this._cbs.indexOf(cb);
|
|
|
if (i >= 0)
|
|
|
this._cbs.splice(i, 1);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private _cancel(reason: any) {
|
|
|
if (this._reason)
|
|
|
return;
|
|
|
|
|
|
this._reason = (reason = reason || new Error("Operation cancelled"));
|
|
|
|
|
|
if (this._cbs) {
|
|
|
this._cbs.forEach(cb => cb(reason));
|
|
|
this._cbs = undefined;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static readonly none: ICancellation = {
|
|
|
isSupported(): boolean {
|
|
|
return false;
|
|
|
},
|
|
|
|
|
|
throwIfRequested(): void {
|
|
|
},
|
|
|
|
|
|
isRequested(): boolean {
|
|
|
return false;
|
|
|
},
|
|
|
|
|
|
register(_cb: (e: any) => void): IDestroyable {
|
|
|
return destroyed;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* Combines multiple cancellation tokens to the single aggregated token.
|
|
|
*
|
|
|
* Aggregated token will be considered as signalled when some tokens are
|
|
|
* signalled. The cancellation callback can be registered with the `register`
|
|
|
* method, it will be fired once with the first signalled token, all other
|
|
|
* tokens will be ignored.
|
|
|
*
|
|
|
* The tokens which don't support cancellation are filtered out, if there are
|
|
|
* no tokens left in the list the method returns `Cancellation.none`.
|
|
|
*
|
|
|
* @param args The list of cancellation tokens to combine
|
|
|
* @returns
|
|
|
*/
|
|
|
static combine(...args: ICancellation[]) {
|
|
|
const tokens = args.filter(ct => ct.isSupported());
|
|
|
return tokens.length > 1 ?
|
|
|
new CancellationAggregate(tokens) :
|
|
|
tokens.length == 1 ? tokens[0] :
|
|
|
this.none;
|
|
|
}
|
|
|
}
|
|
|
|