-
Notifications
You must be signed in to change notification settings - Fork 3k
/
Subject.ts
158 lines (136 loc) · 4.41 KB
/
Subject.ts
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
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';
/**
* 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 {
observers: Observer<T>[] = [];
closed = false;
isStopped = false;
hasError = false;
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();
}
lift<R>(operator: Operator<T, R>): Observable<R> {
const subject = new AnonymousSubject(this, this);
subject.operator = operator as any;
return subject as any;
}
protected _throwIfClosed() {
if (this.closed) {
throw new ObjectUnsubscribedError();
}
}
next(value: T) {
this._throwIfClosed();
if (!this.isStopped) {
const copy = this.observers.slice();
for (const observer of copy) {
observer.next(value);
}
}
}
error(err: any) {
this._throwIfClosed();
if (!this.isStopped) {
this.hasError = this.isStopped = true;
this.thrownError = err;
const { observers } = this;
while (observers.length) {
observers.shift()!.error(err);
}
}
}
complete() {
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 = null!;
}
/** @deprecated This is an internal implementation detail, do not use. */
protected _trySubscribe(subscriber: Subscriber<T>): TeardownLogic {
this._throwIfClosed();
return super._trySubscribe(subscriber);
}
/** @deprecated This is an internal implementation detail, do not use. */
protected _subscribe(subscriber: Subscriber<T>): Subscription {
this._throwIfClosed();
this._checkFinalizedStatuses(subscriber);
return this._innerSubscribe(subscriber);
}
protected _innerSubscribe(subscriber: Subscriber<any>) {
const { hasError, isStopped, observers } = this;
return hasError || isStopped
? EMPTY_SUBSCRIPTION
: (observers.push(subscriber), new Subscription(() => arrRemove(this.observers, subscriber)));
}
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 customize 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(protected 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?.();
}
/** @deprecated This is an internal implementation detail, do not use. */
_subscribe(subscriber: Subscriber<T>): Subscription {
return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;
}
}