问题
I define an Observable like this:
const obs$ = Observable.create(...)
.publishReplay(1)
.refCount();
So that it puts a ReplaySubject(1) between my source Observable and all observers.
Since ReplaySubject has in its state the number of observers (via its observers array property), how is it possible to access the ReplaySubject from obs$?
I actually only need to know if obs$ has any observers or not. RxJS4 had a hasObservers() method on Subject, but it got removed in RxJS5. How can I achieve this with RxJS5?
回答1:
Not sure about your usage but for my needs I created a custom operator that allowed me to transparently perform side-effects (similar to tap) based on the state of the refCount. It just does a pass-through subscription and duck-punches the sub/unsub. The callback gets the current refCount and the previous so that you can tell the state and direction. I like using an operator for this since I can insert it at any point in my stream. If you simply want a binary output for whether there are any subscriptions or not it could be easily modified for that.
const { Observable, Observer, interval } = rxjs;
const { publishReplay, refCount } = rxjs.operators;
const tapRefCount = (onChange) => (source) => {
let refCount = 0;
// mute the operator if it has nothing to do
if (typeof onChange !== 'function') {
return source;
}
// mute errors from side-effects
const safeOnChange = (refCount, prevRefCount) => {
try {
onChange(refCount, prevRefCount);
} catch (e) {
}
};
// spy on subscribe
return Observable.create((observer) => {
const subscription = source.subscribe(observer);
const prevRefCount = refCount;
refCount++;
safeOnChange(refCount, prevRefCount);
// spy on unsubscribe
return () => {
subscription.unsubscribe();
const prevRefCount = refCount;
refCount--;
safeOnChange(refCount, prevRefCount);
};
});
};
const source = interval(1000).pipe(
publishReplay(1),
refCount(),
tapRefCount((refCount, prevRefCount) => { console.log('refCount', refCount, prevRefCount > refCount ? 'down': 'up'); })
);
const firstSub = source.subscribe((x) => { console.log('first', x); });
let secondSub;
setTimeout(() => {
secondSub = source.subscribe((x) => { console.log('second', x); });
}, 1500);
setTimeout(() => {
firstSub.unsubscribe();
}, 4500);
setTimeout(() => {
secondSub.unsubscribe();
}, 5500);
<script src="https://unpkg.com/rxjs@rc/bundles/rxjs.umd.min.js"></script>
The typescript version:
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
export const tapRefCount = (
onChange: (refCount: number, prevRefCount: number) => void
) => <T>(source: Observable<T>): Observable<T> => {
let refCount = 0;
// mute the operator if it has nothing to do
if (typeof onChange !== 'function') {
return source;
}
// mute errors from side-effects
const safeOnChange = (refCount, prevRefCount) => {
try {
onChange(refCount, prevRefCount);
} catch (e) {
}
};
// spy on subscribe
return Observable.create((observer: Observer<T>) => {
const subscription = source.subscribe(observer);
const prevRefCount = refCount;
refCount++;
safeOnChange(refCount, prevRefCount);
// spy on unsubscribe
return () => {
subscription.unsubscribe();
const prevRefCount = refCount;
refCount--;
safeOnChange(refCount, prevRefCount);
};
}) as Observable<T>;
};
回答2:
The Subject class has a public property called observers (see https://github.com/ReactiveX/rxjs/blob/5.5.10/src/Subject.ts#L28)
So you can use just:
const s = new Subject();
...
if (s.observers.length > 0) {
// whatever
}
Be aware that refCount returns an Observable so you won't be able to do what I mentioned above. However, you can provide your own Subject instance to publishReplay as the third argument and use s.observers on that, see http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-publishReplay
来源:https://stackoverflow.com/questions/49976825/check-if-publishreplay-refcount-has-observers-or-not