UNPKG

@reactivex/rxjs

Version:

Reactive Extensions for modern JavaScript

217 lines 9.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var isArray_1 = require("./util/isArray"); var isObject_1 = require("./util/isObject"); var isFunction_1 = require("./util/isFunction"); var UnsubscriptionError_1 = require("./util/UnsubscriptionError"); /** * Represents a disposable resource, such as the execution of an Observable. A * Subscription has one important method, `unsubscribe`, that takes no argument * and just disposes the resource held by the subscription. * * Additionally, subscriptions may be grouped together through the `add()` * method, which will attach a child Subscription to the current Subscription. * When a Subscription is unsubscribed, all its children (and its grandchildren) * will be unsubscribed as well. * * @class Subscription */ var Subscription = /** @class */ (function () { /** * @param {function(): void} [unsubscribe] A function describing how to * perform the disposal of resources when the `unsubscribe` method is called. */ function Subscription(unsubscribe) { /** * A flag to indicate whether this Subscription has already been unsubscribed. * @type {boolean} */ this.closed = false; /** @internal */ this._parentOrParents = null; /** @internal */ this._subscriptions = null; if (unsubscribe) { this._ctorUnsubscribe = true; this._unsubscribe = unsubscribe; } } /** * Disposes the resources held by the subscription. May, for instance, cancel * an ongoing Observable execution or cancel any other type of work that * started when the Subscription was created. * @return {void} */ Subscription.prototype.unsubscribe = function () { var errors; if (this.closed) { return; } var _a = this, _parentOrParents = _a._parentOrParents, _ctorUnsubscribe = _a._ctorUnsubscribe, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; this.closed = true; this._parentOrParents = null; // null out _subscriptions first so any child subscriptions that attempt // to remove themselves from this subscription will noop this._subscriptions = null; if (_parentOrParents instanceof Subscription) { _parentOrParents.remove(this); } else if (_parentOrParents !== null) { for (var index = 0; index < _parentOrParents.length; ++index) { var parent_1 = _parentOrParents[index]; parent_1.remove(this); } } if (isFunction_1.isFunction(_unsubscribe)) { // It's only possible to null _unsubscribe - to release the reference to // any teardown function passed in the constructor - if the property was // actually assigned in the constructor, as there are some classes that // are derived from Subscriber (which derives from Subscription) that // implement an _unsubscribe method as a mechanism for obtaining // unsubscription notifications and some of those subscribers are // recycled. Also, in some of those subscribers, _unsubscribe switches // from a prototype method to an instance property - see notifyNext in // RetryWhenSubscriber. if (_ctorUnsubscribe) { this._unsubscribe = undefined; } try { _unsubscribe.call(this); } catch (e) { errors = e instanceof UnsubscriptionError_1.UnsubscriptionError ? flattenUnsubscriptionErrors(e.errors) : [e]; } } if (isArray_1.isArray(_subscriptions)) { var index = -1; var len = _subscriptions.length; while (++index < len) { var sub = _subscriptions[index]; if (isObject_1.isObject(sub)) { try { sub.unsubscribe(); } catch (e) { errors = errors || []; if (e instanceof UnsubscriptionError_1.UnsubscriptionError) { errors = errors.concat(flattenUnsubscriptionErrors(e.errors)); } else { errors.push(e); } } } } } if (errors) { throw new UnsubscriptionError_1.UnsubscriptionError(errors); } }; /** * Adds a tear down to be called during the unsubscribe() of this * Subscription. Can also be used to add a child subscription. * * If the tear down being added is a subscription that is already * unsubscribed, is the same reference `add` is being called on, or is * `Subscription.EMPTY`, it will not be added. * * If this subscription is already in an `closed` state, the passed * tear down logic will be executed immediately. * * When a parent subscription is unsubscribed, any child subscriptions that were added to it are also unsubscribed. * * @param {TeardownLogic} teardown The additional logic to execute on * teardown. * @return {Subscription} Returns the Subscription used or created to be * added to the inner subscriptions list. This Subscription can be used with * `remove()` to remove the passed teardown logic from the inner subscriptions * list. */ Subscription.prototype.add = function (teardown) { var subscription = teardown; if (!teardown) { return Subscription.EMPTY; } switch (typeof teardown) { case 'function': subscription = new Subscription(teardown); case 'object': if (subscription === this || subscription.closed || typeof subscription.unsubscribe !== 'function') { // This also covers the case where `subscription` is `Subscription.EMPTY`, which is always in `closed` state. return subscription; } else if (this.closed) { subscription.unsubscribe(); return subscription; } else if (!(subscription instanceof Subscription)) { var tmp = subscription; subscription = new Subscription(); subscription._subscriptions = [tmp]; } break; default: { throw new Error('unrecognized teardown ' + teardown + ' added to Subscription.'); } } // Add `this` as parent of `subscription` if that's not already the case. var _parentOrParents = subscription._parentOrParents; if (_parentOrParents === null) { // If we don't have a parent, then set `subscription._parents` to // the `this`, which is the common case that we optimize for. subscription._parentOrParents = this; } else if (_parentOrParents instanceof Subscription) { if (_parentOrParents === this) { // The `subscription` already has `this` as a parent. return subscription; } // If there's already one parent, but not multiple, allocate an // Array to store the rest of the parent Subscriptions. subscription._parentOrParents = [_parentOrParents, this]; } else if (_parentOrParents.indexOf(this) === -1) { // Only add `this` to the _parentOrParents list if it's not already there. _parentOrParents.push(this); } else { // The `subscription` already has `this` as a parent. return subscription; } // Optimize for the common case when adding the first subscription. var subscriptions = this._subscriptions; if (subscriptions === null) { this._subscriptions = [subscription]; } else { subscriptions.push(subscription); } return subscription; }; /** * Removes a Subscription from the internal list of subscriptions that will * unsubscribe during the unsubscribe process of this Subscription. * @param {Subscription} subscription The subscription to remove. * @return {void} */ Subscription.prototype.remove = function (subscription) { var subscriptions = this._subscriptions; if (subscriptions) { var subscriptionIndex = subscriptions.indexOf(subscription); if (subscriptionIndex !== -1) { subscriptions.splice(subscriptionIndex, 1); } } }; /** @nocollapse */ Subscription.EMPTY = (function (empty) { empty.closed = true; return empty; }(new Subscription())); return Subscription; }()); exports.Subscription = Subscription; function flattenUnsubscriptionErrors(errors) { return errors.reduce(function (errs, err) { return errs.concat((err instanceof UnsubscriptionError_1.UnsubscriptionError) ? err.errors : err); }, []); } //# sourceMappingURL=Subscription.js.map