UNPKG

cancellationtoken

Version:

A composable token for cancelling asynchronous operations.

226 lines 8.57 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var NOOP = function () { }; /** * A token that can be passed around to inform consumers of the token that a * certain operation has been cancelled. */ var CancellationToken = /** @class */ (function () { function CancellationToken( /** * Whether the token is already cancelled. */ _isCancelled, /** * Whether the token can be cancelled. */ canBeCancelled) { this._isCancelled = _isCancelled; this.canBeCancelled = canBeCancelled; this._callbacks = new Set(); } Object.defineProperty(CancellationToken.prototype, "isCancelled", { /** * Whether the token has been cancelled. */ get: function () { return this._isCancelled; }, enumerable: false, configurable: true }); Object.defineProperty(CancellationToken.prototype, "reason", { /** * Why this token has been cancelled. */ get: function () { if (this.isCancelled) { return this._reason; } else { throw new Error('This token is not cancelled.'); } }, enumerable: false, configurable: true }); /** * Make a promise that resolves when the async operation resolves, * or rejects when the operation is rejected or this token is cancelled. */ CancellationToken.prototype.racePromise = function (asyncOperation) { var _this = this; if (!this.canBeCancelled) { return asyncOperation; } return new Promise(function (resolve, reject) { // we could use Promise.finally here as soon as it's implemented in the major browsers var unregister = _this.onCancelled(function (reason) { return reject(new CancellationToken.CancellationError(reason)); }); asyncOperation.then(function (value) { resolve(value); unregister(); }, function (err) { reject(err); unregister(); }); }); }; /** * Throw a {CancellationToken.CancellationError} if this token is cancelled. */ CancellationToken.prototype.throwIfCancelled = function () { if (this._isCancelled) { throw new CancellationToken.CancellationError(this._reason); } }; /** * Invoke the callback when this token is cancelled. * If this token is already cancelled, the callback is invoked immediately. * Returns a function that unregisters the cancellation callback. */ CancellationToken.prototype.onCancelled = function (cb) { var _this = this; var _a; if (!this.canBeCancelled) { return NOOP; } if (this.isCancelled) { cb(this.reason); return NOOP; } /* istanbul ignore next */ (_a = this._callbacks) === null || _a === void 0 ? void 0 : _a.add(cb); return function () { var _a; return (_a = _this._callbacks) === null || _a === void 0 ? void 0 : _a.delete(cb); }; }; /** * Create a {CancellationToken} and a method that cancels it. */ CancellationToken.create = function () { var token = new CancellationToken(false, true); var cancel = function (reason) { var _a; if (token._isCancelled) return; token._isCancelled = true; token._reason = reason; /* istanbul ignore next */ (_a = token._callbacks) === null || _a === void 0 ? void 0 : _a.forEach(function (cb) { return cb(reason); }); delete token._callbacks; // release memory }; return { token: token, cancel: cancel }; }; /** * Create a {CancellationToken} and a method that cancels it. * The token will be cancelled automatically after the specified timeout in milliseconds. */ CancellationToken.timeout = function (ms) { var _a = CancellationToken.create(), token = _a.token, originalCancel = _a.cancel; var timer = setTimeout(function () { return originalCancel(CancellationToken.timeout); }, ms); var cancel = function (reason) { if (token._isCancelled) return; clearTimeout(timer); originalCancel(reason); }; return { token: token, cancel: cancel }; }; /** * Create a {CancellationToken} that is cancelled when all of the given tokens are cancelled. * * This is like {Promise<T>.all} for {CancellationToken}s. */ CancellationToken.all = function () { var tokens = []; for (var _i = 0; _i < arguments.length; _i++) { tokens[_i] = arguments[_i]; } // If *any* of the tokens cannot be cancelled, then the token we return can never be. if (tokens.some(function (token) { return !token.canBeCancelled; })) { return CancellationToken.CONTINUE; } var combined = CancellationToken.create(); var countdown = tokens.length; var handleNextTokenCancelled = function () { if (--countdown === 0) { var reasons = tokens.map(function (token) { return token._reason; }); combined.cancel(reasons); } }; tokens.forEach(function (token) { return token.onCancelled(handleNextTokenCancelled); }); return combined.token; }; /** * Create a {CancellationToken} that is cancelled when at least one of the given tokens is cancelled. * * This is like {Promise<T>.race} for {CancellationToken}s. */ CancellationToken.race = function () { var tokens = []; for (var _i = 0; _i < arguments.length; _i++) { tokens[_i] = arguments[_i]; } // If *any* of the tokens is already cancelled, immediately return that token. for (var _a = 0, tokens_1 = tokens; _a < tokens_1.length; _a++) { var token = tokens_1[_a]; if (token._isCancelled) { return token; } } var combined = CancellationToken.create(); var unregistrations; var handleAnyTokenCancelled = function (reason) { unregistrations.forEach(function (unregister) { return unregister(); }); // release memory combined.cancel(reason); }; unregistrations = tokens.map(function (token) { return token.onCancelled(handleAnyTokenCancelled); }); return combined.token; }; /** * A cancellation token that is already cancelled. */ CancellationToken.CANCELLED = new CancellationToken(true, true); /** * A cancellation token that is never cancelled. */ CancellationToken.CONTINUE = new CancellationToken(false, false); return CancellationToken; }()); /* istanbul ignore next */ (function (CancellationToken) { /** * The error that is thrown when a {CancellationToken} has been cancelled and a * consumer of the token calls {CancellationToken.throwIfCancelled} on it. */ var CancellationError = /** @class */ (function (_super) { __extends(CancellationError, _super); function CancellationError( /** * The reason why the token was cancelled. */ reason) { var _this = _super.call(this, 'Operation cancelled') || this; _this.reason = reason; Object.setPrototypeOf(_this, CancellationError.prototype); return _this; } return CancellationError; }(Error)); CancellationToken.CancellationError = CancellationError; })(CancellationToken || (CancellationToken = {})); exports.default = CancellationToken; //# sourceMappingURL=CancellationToken.js.map