1- import { isArray } from './util/isArray' ;
21import { isObject } from './util/isObject' ;
32import { isFunction } from './util/isFunction' ;
43import { tryCatch } from './util/tryCatch' ;
@@ -40,7 +39,9 @@ export class Subscription implements ISubscription {
4039 */
4140 public closed : boolean = false ;
4241
43- private _subscriptions : ISubscription [ ] ;
42+ protected _parent : Subscription = null ;
43+ protected _parents : Subscription [ ] = null ;
44+ private _subscriptions : ISubscription [ ] = null ;
4445
4546 /**
4647 * @param {function(): void } [unsubscribe] A function describing how to
@@ -66,11 +67,25 @@ export class Subscription implements ISubscription {
6667 return ;
6768 }
6869
69- this . closed = true ;
70-
71- const { _unsubscribe, _subscriptions } = ( < any > this ) ;
70+ let { _parent, _parents, _unsubscribe, _subscriptions } = ( < any > this ) ;
7271
73- ( < any > this ) . _subscriptions = null ;
72+ this . closed = true ;
73+ this . _parent = null ;
74+ this . _parents = null ;
75+ // null out _subscriptions first so any child subscriptions that attempt
76+ // to remove themselves from this subscription will noop
77+ this . _subscriptions = null ;
78+
79+ let index = - 1 , len = _parents && _parents . length || 0 ;
80+
81+ // if this._parent is null, then so is this._parents, and we
82+ // don't have to remove ourselves from any parent subscriptions.
83+ while ( _parent ) {
84+ _parent . remove ( this ) ;
85+ // if this._parents is null or index >= len,
86+ // then _parent is set to false, and the loop exits
87+ _parent = ++ index < len && _parents [ index ] ;
88+ }
7489
7590 if ( isFunction ( _unsubscribe ) ) {
7691 let trial = tryCatch ( _unsubscribe ) . call ( this ) ;
@@ -83,10 +98,10 @@ export class Subscription implements ISubscription {
8398 }
8499 }
85100
86- if ( isArray ( _subscriptions ) ) {
101+ if ( _subscriptions ) {
87102
88- let index = - 1 ;
89- const len = _subscriptions . length ;
103+ index = - 1 ;
104+ len = _subscriptions . length ;
90105
91106 while ( ++ index < len ) {
92107 const sub = _subscriptions [ index ] ;
@@ -138,27 +153,33 @@ export class Subscription implements ISubscription {
138153 return this ;
139154 }
140155
141- let sub = ( < Subscription > teardown ) ;
156+ let subscription = ( < Subscription > teardown ) ;
142157
143158 switch ( typeof teardown ) {
144159 case 'function' :
145- sub = new Subscription ( < ( ( ) => void ) > teardown ) ;
160+ subscription = new Subscription ( < ( ( ) => void ) > teardown ) ;
146161 case 'object' :
147- if ( sub . closed || typeof sub . unsubscribe !== 'function' ) {
148- return sub ;
162+ if ( subscription . closed || typeof subscription . unsubscribe !== 'function' ) {
163+ return subscription ;
149164 } else if ( this . closed ) {
150- sub . unsubscribe ( ) ;
151- return sub ;
165+ subscription . unsubscribe ( ) ;
166+ return subscription ;
167+ } else if ( typeof subscription . _addParent !== 'function' /* quack quack */ ) {
168+ const tmp = subscription ;
169+ subscription = new Subscription ( ) ;
170+ subscription . _subscriptions = [ tmp ] ;
152171 }
153172 break ;
154173 default :
155174 throw new Error ( 'unrecognized teardown ' + teardown + ' added to Subscription.' ) ;
156175 }
157176
158- const childSub = new ChildSubscription ( sub , this ) ;
159- this . _subscriptions = this . _subscriptions || [ ] ;
160- this . _subscriptions . push ( childSub ) ;
161- return childSub ;
177+ const subscriptions = this . _subscriptions || ( this . _subscriptions = [ ] ) ;
178+
179+ subscriptions . push ( subscription ) ;
180+ subscription . _addParent ( this ) ;
181+
182+ return subscription ;
162183 }
163184
164185 /**
@@ -168,37 +189,25 @@ export class Subscription implements ISubscription {
168189 * @return {void }
169190 */
170191 remove ( subscription : Subscription ) : void {
171-
172- // HACK: This might be redundant because of the logic in `add()`
173- if ( subscription == null || (
174- subscription === this ) || (
175- subscription === Subscription . EMPTY ) ) {
176- return ;
177- }
178-
179- const subscriptions = ( < any > this ) . _subscriptions ;
180-
192+ const subscriptions = this . _subscriptions ;
181193 if ( subscriptions ) {
182194 const subscriptionIndex = subscriptions . indexOf ( subscription ) ;
183195 if ( subscriptionIndex !== - 1 ) {
184196 subscriptions . splice ( subscriptionIndex , 1 ) ;
185197 }
186198 }
187199 }
188- }
189-
190- export class ChildSubscription extends Subscription {
191- constructor ( private _innerSub : ISubscription , private _parent : Subscription ) {
192- super ( ) ;
193- }
194200
195- _unsubscribe ( ) {
196- const { _innerSub, _parent } = this ;
197- _parent . remove ( this ) ;
198- _innerSub . unsubscribe ( ) ;
201+ private _addParent ( parent : Subscription ) {
202+ let { _parent, _parents } = this ;
203+ if ( ! _parent ) {
204+ this . _parent = parent ;
205+ } else if ( _parents || ( _parents = this . _parents = [ ] ) ) {
206+ _parents . push ( this ) ;
207+ }
199208 }
200209}
201210
202211function flattenUnsubscriptionErrors ( errors : any [ ] ) {
203212 return errors . reduce ( ( errs , err ) => errs . concat ( ( err instanceof UnsubscriptionError ) ? err . errors : err ) , [ ] ) ;
204- }
213+ }
0 commit comments