rxjs
Version:
Reactive Extensions for modern JavaScript
84 lines (81 loc) • 3.36 kB
text/typescript
import { Subscriber } from '../Subscriber';
/**
* A generic helper for allowing operators to be created with a Subscriber and
* use closures to capture necessary state from the operator function itself.
*/
export class OperatorSubscriber<T> extends Subscriber<T> {
/**
* Creates an instance of an `OperatorSubscriber`.
* @param destination The downstream subscriber.
* @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any
* error that occurs in this function is caught and sent to the `error` method of this subscriber.
* @param onError Handles errors from the subscription, any errors that occur in this handler are caught
* and send to the `destination` error handler.
* @param onComplete Handles completion notification from the subscription. Any errors that occur in
* this handler are sent to the `destination` error handler.
* @param onFinalize Additional teardown logic here. This will only be called on teardown if the
* subscriber itself is not already closed. This is called after all other teardown logic is executed.
*/
constructor(
destination: Subscriber<any>,
onNext?: (value: T) => void,
onComplete?: () => void,
onError?: (err: any) => void,
private onFinalize?: () => void
) {
// It's important - for performance reasons - that all of this class's
// members are initialized and that they are always initialized in the same
// order. This will ensure that all OperatorSubscriber instances have the
// same hidden class in V8. This, in turn, will help keep the number of
// hidden classes involved in property accesses within the base class as
// low as possible. If the number of hidden classes involved exceeds four,
// the property accesses will become megamorphic and performance penalties
// will be incurred - i.e. inline caches won't be used.
//
// The reasons for ensuring all instances have the same hidden class are
// further discussed in this blog post from Benedikt Meurer:
// https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/
super(destination);
this._next = onNext
? function (this: OperatorSubscriber<T>, value: T) {
try {
onNext(value);
} catch (err) {
destination.error(err);
}
}
: super._next;
this._error = onError
? function (this: OperatorSubscriber<T>, err: any) {
try {
onError(err);
} catch (err) {
// Send any errors that occur down stream.
destination.error(err);
} finally {
// Ensure teardown.
this.unsubscribe();
}
}
: super._error;
this._complete = onComplete
? function (this: OperatorSubscriber<T>) {
try {
onComplete();
} catch (err) {
// Send any errors that occur down stream.
destination.error(err);
} finally {
// Ensure teardown.
this.unsubscribe();
}
}
: super._complete;
}
unsubscribe() {
const { closed } = this;
super.unsubscribe();
// Execute additional teardown if we have any and we didn't already do so.
!closed && this.onFinalize?.();
}
}