UNPKG

async-promise

Version:

Asynchronous coordination primitives for JavaScript and TypeScript

235 lines (232 loc) 8.47 kB
/*! ***************************************************************************** Copyright (C) Ron A. Buckton. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ***************************************************************************** */ var trace_1 = require('./trace'); var list_1 = require('./list'); var task_1 = require('./task'); var emptyRegistration = Object.freeze({ unregister: function () { } }); /** * A source for cancellation */ var CancellationTokenSource = (function () { /** * @param links Other `CancellationToken` instances that will cancel this source if the tokens are canceled. */ function CancellationTokenSource(links) { var _this = this; this._token = new CancellationToken(this); if (links != undefined) { this._links = new Array(); for (var _i = 0; _i < links.length; _i++) { var link = links[_i]; if (!link) { continue; } if (link.canceled) { this.cancel(link.reason); return; } this._links.push(link.register(function (reason) { _this.cancel(reason); })); } } } Object.defineProperty(CancellationTokenSource.prototype, "token", { /** * Gets the `CancellationToken` for this source. */ get: function () { return this._token; }, enumerable: true, configurable: true }); /** * Signals the source is cancelled. * @param reason An optional reason for the cancellation. */ CancellationTokenSource.prototype.cancel = function (reason) { if (this._canceled) { return; } this._throwIfFrozen(); if (reason == undefined) { reason = new Error("operation was canceled."); } if (reason instanceof Error && !("stack" in reason)) { trace_1.Trace.setNonUserCodeExceptions = true; try { throw reason; } catch (error) { reason = error; } } var callbacks = this._callbacks; this._canceled = true; this._reason = reason; this._callbacks = undefined; Object.freeze(this); if (callbacks) { for (var node = callbacks.first; node; node = node.next) { this._enqueueCallback(node.value); } callbacks.clear(); } }; /** * Closes the CancellationSource, preventing any future cancellation signal. */ CancellationTokenSource.prototype.close = function () { if (Object.isFrozen(this)) { return; } if (this._links != undefined) { for (var _i = 0, _a = this._links; _i < _a.length; _i++) { var link = _a[_i]; link.unregister(); } } if (this._callbacks != undefined) { this._callbacks.clear(); } this._links = undefined; this._callbacks = undefined; Object.freeze(this); }; CancellationTokenSource.prototype._register = function (callback) { if (this._canceled) { callback(this._reason); return emptyRegistration; } if (Object.isFrozen(this)) { return emptyRegistration; } var callbacks = this._callbacks; if (callbacks == undefined) { callbacks = new list_1.LinkedList(); this._callbacks = callbacks; } var node = new list_1.LinkedListNode(callback); callbacks.push(node); return Object.freeze({ unregister: function () { callbacks.delete(node); } }); }; CancellationTokenSource.prototype._enqueueCallback = function (callback) { var _this = this; var id = trace_1.Trace.msTraceAsyncOperationStarting("CancellationTokenSource.cancel"); task_1.TaskScheduler.default.enqueue(task_1.TaskSchedulerPriority.send, function () { _this._invokeCallback(callback, id); }); }; CancellationTokenSource.prototype._invokeCallback = function (callback, id) { trace_1.Trace.msTraceAsyncOperationCompleted(id, trace_1.Trace.MS_ASYNC_OP_STATUS_SUCCESS); trace_1.Trace.msTraceAsyncCallbackStarting(id); try { callback(this._reason); } finally { trace_1.Trace.msTraceAsyncCallbackCompleted(); } }; CancellationTokenSource.prototype._throwIfFrozen = function () { if (Object.isFrozen(this)) { throw new Error("cannot modify a closed source"); } }; return CancellationTokenSource; })(); exports.CancellationTokenSource = CancellationTokenSource; /** * A token used to recieve a cancellation signal. */ var CancellationToken = (function () { /*@internal*/ function CancellationToken(source) { this._source = source; Object.freeze(this); } Object.defineProperty(CancellationToken, "none", { /** * Gets an empty cancellation token that will never be canceled. */ get: function () { if (CancellationToken._none == undefined) { CancellationToken._none = new CancellationToken(undefined); } return CancellationToken._none; }, enumerable: true, configurable: true }); Object.defineProperty(CancellationToken.prototype, "canBeCanceled", { /** * Gets a value indicating whether the token can be canceled. */ get: function () { return this._source != undefined && !Object.isFrozen(this._source); }, enumerable: true, configurable: true }); Object.defineProperty(CancellationToken.prototype, "canceled", { /** * Gets a value indicating whether the token has received a cancellation signal. */ get: function () { if (this._source == undefined) { return false; } return this._source._canceled; }, enumerable: true, configurable: true }); Object.defineProperty(CancellationToken.prototype, "reason", { /** * Gets the reason for cancellation, if one was supplied. */ get: function () { if (this._source == undefined) { return undefined; } return this._source._reason; }, enumerable: true, configurable: true }); /** * Throws an `Error` if the token has received a cancellation signal. */ CancellationToken.prototype.throwIfCanceled = function (reason) { if (reason === void 0) { reason = this.reason; } if (this._source == undefined) { return; } if (this.canceled) { throw reason; } }; /** * Requests a callback when the token receives a cancellation signal to perform additional cleanup. * @param callback The callback to execute * @returns A `CancellationTokenRegistration` that that can be used to cancel the cleanup request. */ CancellationToken.prototype.register = function (callback) { if (typeof callback !== "function") { throw new TypeError("Argument is not a Function object"); } if (this._source == undefined) { return emptyRegistration; } return this._source._register(callback); }; return CancellationToken; })(); exports.CancellationToken = CancellationToken;