File size: 5,561 Bytes
bc20498 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
import { Operator } from './Operator';
import { Observable } from './Observable';
import { Subscriber } from './Subscriber';
import { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';
import { Observer, SubscriptionLike, TeardownLogic } from './types';
import { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';
import { arrRemove } from './util/arrRemove';
import { errorContext } from './util/errorContext';
/**
* A Subject is a special type of Observable that allows values to be
* multicasted to many Observers. Subjects are like EventEmitters.
*
* Every Subject is an Observable and an Observer. You can subscribe to a
* Subject, and you can call next to feed values as well as error and complete.
*/
export class Subject<T> extends Observable<T> implements SubscriptionLike {
closed = false;
private currentObservers: Observer<T>[] | null = null;
/** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */
observers: Observer<T>[] = [];
/** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */
isStopped = false;
/** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */
hasError = false;
/** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */
thrownError: any = null;
/**
* Creates a "subject" by basically gluing an observer to an observable.
*
* @nocollapse
* @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.
*/
static create: (...args: any[]) => any = <T>(destination: Observer<T>, source: Observable<T>): AnonymousSubject<T> => {
return new AnonymousSubject<T>(destination, source);
};
constructor() {
// NOTE: This must be here to obscure Observable's constructor.
super();
}
/** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */
lift<R>(operator: Operator<T, R>): Observable<R> {
const subject = new AnonymousSubject(this, this);
subject.operator = operator as any;
return subject as any;
}
/** @internal */
protected _throwIfClosed() {
if (this.closed) {
throw new ObjectUnsubscribedError();
}
}
next(value: T) {
errorContext(() => {
this._throwIfClosed();
if (!this.isStopped) {
if (!this.currentObservers) {
this.currentObservers = Array.from(this.observers);
}
for (const observer of this.currentObservers) {
observer.next(value);
}
}
});
}
error(err: any) {
errorContext(() => {
this._throwIfClosed();
if (!this.isStopped) {
this.hasError = this.isStopped = true;
this.thrownError = err;
const { observers } = this;
while (observers.length) {
observers.shift()!.error(err);
}
}
});
}
complete() {
errorContext(() => {
this._throwIfClosed();
if (!this.isStopped) {
this.isStopped = true;
const { observers } = this;
while (observers.length) {
observers.shift()!.complete();
}
}
});
}
unsubscribe() {
this.isStopped = this.closed = true;
this.observers = this.currentObservers = null!;
}
get observed() {
return this.observers?.length > 0;
}
/** @internal */
protected _trySubscribe(subscriber: Subscriber<T>): TeardownLogic {
this._throwIfClosed();
return super._trySubscribe(subscriber);
}
/** @internal */
protected _subscribe(subscriber: Subscriber<T>): Subscription {
this._throwIfClosed();
this._checkFinalizedStatuses(subscriber);
return this._innerSubscribe(subscriber);
}
/** @internal */
protected _innerSubscribe(subscriber: Subscriber<any>) {
const { hasError, isStopped, observers } = this;
if (hasError || isStopped) {
return EMPTY_SUBSCRIPTION;
}
this.currentObservers = null;
observers.push(subscriber);
return new Subscription(() => {
this.currentObservers = null;
arrRemove(observers, subscriber);
});
}
/** @internal */
protected _checkFinalizedStatuses(subscriber: Subscriber<any>) {
const { hasError, thrownError, isStopped } = this;
if (hasError) {
subscriber.error(thrownError);
} else if (isStopped) {
subscriber.complete();
}
}
/**
* Creates a new Observable with this Subject as the source. You can do this
* to create custom Observer-side logic of the Subject and conceal it from
* code that uses the Observable.
* @return {Observable} Observable that the Subject casts to
*/
asObservable(): Observable<T> {
const observable: any = new Observable<T>();
observable.source = this;
return observable;
}
}
/**
* @class AnonymousSubject<T>
*/
export class AnonymousSubject<T> extends Subject<T> {
constructor(
/** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */
public destination?: Observer<T>,
source?: Observable<T>
) {
super();
this.source = source;
}
next(value: T) {
this.destination?.next?.(value);
}
error(err: any) {
this.destination?.error?.(err);
}
complete() {
this.destination?.complete?.();
}
/** @internal */
protected _subscribe(subscriber: Subscriber<T>): Subscription {
return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;
}
}
|