@@ -147,6 +147,49 @@ function bind<Fn extends (...args: any[]) => any>(fn: Fn, thisArg: any): Fn {
147147 return _bind . call ( fn , thisArg ) ;
148148}
149149
150+ /**
151+ * Internal optimization only, DO NOT EXPOSE.
152+ * @internal
153+ */
154+ class ConsumerObserver < T > implements Observer < T > {
155+ constructor ( private partialObserver : Partial < Observer < T > > ) { }
156+
157+ next ( value : T ) : void {
158+ const { partialObserver } = this ;
159+ if ( partialObserver . next ) {
160+ try {
161+ partialObserver . next ( value ) ;
162+ } catch ( error ) {
163+ handleUnhandledError ( error ) ;
164+ }
165+ }
166+ }
167+
168+ error ( err : any ) : void {
169+ const { partialObserver } = this ;
170+ if ( partialObserver . error ) {
171+ try {
172+ partialObserver . error ( err ) ;
173+ } catch ( error ) {
174+ handleUnhandledError ( error ) ;
175+ }
176+ } else {
177+ handleUnhandledError ( err ) ;
178+ }
179+ }
180+
181+ complete ( ) : void {
182+ const { partialObserver } = this ;
183+ if ( partialObserver . complete ) {
184+ try {
185+ partialObserver . complete ( ) ;
186+ } catch ( error ) {
187+ handleUnhandledError ( error ) ;
188+ }
189+ }
190+ }
191+ }
192+
150193export class SafeSubscriber < T > extends Subscriber < T > {
151194 constructor (
152195 observerOrNext ?: Partial < Observer < T > > | ( ( value : T ) => void ) | null ,
@@ -155,65 +198,51 @@ export class SafeSubscriber<T> extends Subscriber<T> {
155198 ) {
156199 super ( ) ;
157200
158- let next : ( ( value : T ) => void ) | undefined ;
159- if ( isFunction ( observerOrNext ) ) {
201+ let partialObserver : Partial < Observer < T > > ;
202+ if ( isFunction ( observerOrNext ) || ! observerOrNext ) {
160203 // The first argument is a function, not an observer. The next
161204 // two arguments *could* be observers, or they could be empty.
162- next = observerOrNext ;
163- } else if ( observerOrNext ) {
164- // The first argument is an observer object, we have to pull the handlers
165- // off and capture the owner object as the context. That is because we're
166- // going to put them all in a new destination with ensured methods
167- // for `next`, `error`, and `complete`. That's part of what makes this
168- // the "Safe" Subscriber.
169- ( { next, error, complete } = observerOrNext ) ;
205+ partialObserver = {
206+ next : observerOrNext ?? undefined ,
207+ error : error ?? undefined ,
208+ complete : complete ?? undefined ,
209+ } ;
210+ } else {
211+ // The first argument is a partial observer.
170212 let context : any ;
171213 if ( this && config . useDeprecatedNextContext ) {
172214 // This is a deprecated path that made `this.unsubscribe()` available in
173215 // next handler functions passed to subscribe. This only exists behind a flag
174216 // now, as it is *very* slow.
175217 context = Object . create ( observerOrNext ) ;
176218 context . unsubscribe = ( ) => this . unsubscribe ( ) ;
219+ partialObserver = {
220+ next : observerOrNext . next && bind ( observerOrNext . next , context ) ,
221+ error : observerOrNext . error && bind ( observerOrNext . error , context ) ,
222+ complete : observerOrNext . complete && bind ( observerOrNext . complete , context ) ,
223+ } ;
177224 } else {
178- context = observerOrNext ;
225+ // The "normal" path. Just use the partial observer directly.
226+ partialObserver = observerOrNext ;
179227 }
180- next = next && bind ( next , context ) ;
181- error = error && bind ( error , context ) ;
182- complete = complete && bind ( complete , context ) ;
183228 }
184229
185- // Once we set the destination, the superclass `Subscriber` will
186- // do it's magic in the `_next`, `_error`, and `_complete` methods.
187- this . destination = {
188- next : next ? wrapForErrorHandling ( next , this ) : noop ,
189- error : wrapForErrorHandling ( error ?? defaultErrorHandler , this ) ,
190- complete : complete ? wrapForErrorHandling ( complete , this ) : noop ,
191- } ;
230+ // Wrap the partial observer to ensure it's a full observer, and
231+ // make sure proper error handling is accounted for.
232+ this . destination = new ConsumerObserver ( partialObserver ) ;
192233 }
193234}
194235
195- /**
196- * Wraps a user-provided handler (or our {@link defaultErrorHandler} in one case) to
197- * ensure that any thrown errors are caught and handled appropriately.
198- *
199- * @param handler The handler to wrap
200- * @param instance The SafeSubscriber instance we're going to mark if there's an error.
201- */
202- function wrapForErrorHandling ( handler : ( arg ?: any ) => void , instance : SafeSubscriber < any > ) {
203- return ( ...args : any [ ] ) => {
204- try {
205- handler ( ...args ) ;
206- } catch ( err ) {
207- if ( config . useDeprecatedSynchronousErrorHandling ) {
208- captureError ( err ) ;
209- } else {
210- // Ideal path, we report this as an unhandled error,
211- // which is thrown on a new call stack.
212- reportUnhandledError ( err ) ;
213- }
214- }
215- } ;
236+ function handleUnhandledError ( error : any ) {
237+ if ( config . useDeprecatedSynchronousErrorHandling ) {
238+ captureError ( error ) ;
239+ } else {
240+ // Ideal path, we report this as an unhandled error,
241+ // which is thrown on a new call stack.
242+ reportUnhandledError ( error ) ;
243+ }
216244}
245+
217246/**
218247 * An error handler used when no error handler was supplied
219248 * to the SafeSubscriber -- meaning no error handler was supplied
0 commit comments