UNPKG

@reactivex/rxjs

Version:

Reactive Extensions for modern JavaScript

238 lines (188 loc) 5.34 kB
import {Operator} from './Operator'; import {Observer} from './Observer'; import {Observable} from './Observable'; import {Subscriber} from './Subscriber'; import {Subscription, ISubscription, TeardownLogic} from './Subscription'; import {SubjectSubscription} from './SubjectSubscription'; import {$$rxSubscriber} from './symbol/rxSubscriber'; import {throwError} from './util/throwError'; import {ObjectUnsubscribedError} from './util/ObjectUnsubscribedError'; /** * @class Subject<T> */ export class Subject<T> extends Observable<T> implements Observer<T>, ISubscription { static create: Function = <T>(destination: Observer<T>, source: Observable<T>): Subject<T> => { return new Subject<T>(destination, source); }; constructor(protected destination?: Observer<T>, protected source?: Observable<T>) { super(); this.source = source; } public observers: Observer<T>[] = []; public isUnsubscribed: boolean = false; protected isStopped: boolean = false; protected hasErrored: boolean = false; protected errorValue: any; protected dispatching: boolean = false; protected hasCompleted: boolean = false; lift<T, R>(operator: Operator<T, R>): Observable<T> { const subject = new Subject(this.destination || this, this); subject.operator = operator; return <any>subject; } add(subscription: TeardownLogic): Subscription { return Subscription.prototype.add.call(this, subscription); } remove(subscription: Subscription): void { Subscription.prototype.remove.call(this, subscription); } unsubscribe(): void { Subscription.prototype.unsubscribe.call(this); } protected _subscribe(subscriber: Subscriber<T>): TeardownLogic { if (this.source) { return this.source.subscribe(subscriber); } else { if (subscriber.isUnsubscribed) { return; } else if (this.hasErrored) { return subscriber.error(this.errorValue); } else if (this.hasCompleted) { return subscriber.complete(); } this.throwIfUnsubscribed(); const subscription = new SubjectSubscription(this, subscriber); this.observers.push(subscriber); return subscription; } } protected _unsubscribe(): void { this.source = null; this.isStopped = true; this.observers = null; this.destination = null; } next(value: T): void { this.throwIfUnsubscribed(); if (this.isStopped) { return; } this.dispatching = true; this._next(value); this.dispatching = false; if (this.hasErrored) { this._error(this.errorValue); } else if (this.hasCompleted) { this._complete(); } } error(err?: any): void { this.throwIfUnsubscribed(); if (this.isStopped) { return; } this.isStopped = true; this.hasErrored = true; this.errorValue = err; if (this.dispatching) { return; } this._error(err); } complete(): void { this.throwIfUnsubscribed(); if (this.isStopped) { return; } this.isStopped = true; this.hasCompleted = true; if (this.dispatching) { return; } this._complete(); } asObservable(): Observable<T> { const observable = new SubjectObservable(this); return observable; } protected _next(value: T): void { if (this.destination) { this.destination.next(value); } else { this._finalNext(value); } } protected _finalNext(value: T): void { let index = -1; const observers = this.observers.slice(0); const len = observers.length; while (++index < len) { observers[index].next(value); } } protected _error(err: any): void { if (this.destination) { this.destination.error(err); } else { this._finalError(err); } } protected _finalError(err: any): void { let index = -1; const observers = this.observers; // optimization to block our SubjectSubscriptions from // splicing themselves out of the observers list one by one. this.observers = null; this.isUnsubscribed = true; if (observers) { const len = observers.length; while (++index < len) { observers[index].error(err); } } this.isUnsubscribed = false; this.unsubscribe(); } protected _complete(): void { if (this.destination) { this.destination.complete(); } else { this._finalComplete(); } } protected _finalComplete(): void { let index = -1; const observers = this.observers; // optimization to block our SubjectSubscriptions from // splicing themselves out of the observers list one by one. this.observers = null; this.isUnsubscribed = true; if (observers) { const len = observers.length; while (++index < len) { observers[index].complete(); } } this.isUnsubscribed = false; this.unsubscribe(); } private throwIfUnsubscribed(): void { if (this.isUnsubscribed) { throwError(new ObjectUnsubscribedError()); } } [$$rxSubscriber]() { return new Subscriber<T>(this); } } /** * We need this JSDoc comment for affecting ESDoc. * @ignore * @extends {Ignored} */ class SubjectObservable<T> extends Observable<T> { constructor(source: Subject<T>) { super(); this.source = source; } }