import { Operator } from './Operator'; import { Observer } from './Observer'; import { Observable } from './Observable'; import { Subscriber } from './Subscriber'; import { ISubscription, Subscription, TeardownLogic } from './Subscription'; import { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError'; import { SubjectSubscription } from './SubjectSubscription'; import { rxSubscriber as rxSubscriberSymbol } from './symbol/rxSubscriber'; /** * @class SubjectSubscriber */ export class SubjectSubscriber extends Subscriber { constructor(protected destination: Subject) { super(destination); } } /** * @class Subject */ export class Subject extends Observable implements ISubscription { [rxSubscriberSymbol]() { return new SubjectSubscriber(this); } observers: Observer[] = []; closed = false; isStopped = false; hasError = false; thrownError: any = null; constructor() { super(); } static create: Function = (destination: Observer, source: Observable): AnonymousSubject => { return new AnonymousSubject(destination, source); } lift(operator: Operator): Observable { const subject = new AnonymousSubject(this, this); subject.operator = operator; return subject; } next(value?: T) { if (this.closed) { throw new ObjectUnsubscribedError(); } if (!this.isStopped) { const { observers } = this; const len = observers.length; const copy = observers.slice(); for (let i = 0; i < len; i++) { copy[i].next(value); } } } error(err: any) { if (this.closed) { throw new ObjectUnsubscribedError(); } this.hasError = true; this.thrownError = err; this.isStopped = true; const { observers } = this; const len = observers.length; const copy = observers.slice(); for (let i = 0; i < len; i++) { copy[i].error(err); } this.observers.length = 0; } complete() { if (this.closed) { throw new ObjectUnsubscribedError(); } this.isStopped = true; const { observers } = this; const len = observers.length; const copy = observers.slice(); for (let i = 0; i < len; i++) { copy[i].complete(); } this.observers.length = 0; } unsubscribe() { this.isStopped = true; this.closed = true; this.observers = null; } protected _trySubscribe(subscriber: Subscriber): TeardownLogic { if (this.closed) { throw new ObjectUnsubscribedError(); } else { return super._trySubscribe(subscriber); } } /** @deprecated internal use only */ _subscribe(subscriber: Subscriber): Subscription { if (this.closed) { throw new ObjectUnsubscribedError(); } else if (this.hasError) { subscriber.error(this.thrownError); return Subscription.EMPTY; } else if (this.isStopped) { subscriber.complete(); return Subscription.EMPTY; } else { this.observers.push(subscriber); return new SubjectSubscription(this, subscriber); } } asObservable(): Observable { const observable = new Observable(); (observable).source = this; return observable; } } /** * @class AnonymousSubject */ export class AnonymousSubject extends Subject { constructor(protected destination?: Observer, source?: Observable) { super(); this.source = source; } next(value: T) { const { destination } = this; if (destination && destination.next) { destination.next(value); } } error(err: any) { const { destination } = this; if (destination && destination.error) { this.destination.error(err); } } complete() { const { destination } = this; if (destination && destination.complete) { this.destination.complete(); } } /** @deprecated internal use only */ _subscribe(subscriber: Subscriber): Subscription { const { source } = this; if (source) { return this.source.subscribe(subscriber); } else { return Subscription.EMPTY; } } }