In your case you could make good use of RxJS Subjects with refCounting. You would give the Subject your source observable and let refCount manage its subscriptions.
refCount will unsubscribe from your source observable if there are no observers listening to it. On the other hand it creates new instance of source observable if observer count was 0 and an observer subscribed to it.
Subject would act as a proxy between the observer(s) and the source observable and manage subscribing and unsubscribing from it. In essence how it would work is when you have your first observer subscribe to your Subject the Subject in turn subscribes to your source observable (refCount went from 0 to 1). Subjects allow multiple observers listening to the unicast source observable making it multicast. When observers start to unsubscribe and the refCount falls down to 0 again the Subject itself will unsubscribe from source observable.
It's better understood in code:
const {
Observable
} = Rx;
let sourceObservable = Observable
.create((observer) => {
let count = 0;
let interval = setInterval(() => {
observer.next(count++)
}, 700);
setTimeout(() => {
clearInterval(interval);
observer.complete();
}, 5500);
return () => {
clearInterval(interval);
console.log('######## Source observable unsubscribed');
}
})
.do((x) => console.log('#### Source emits: ' + x));
let subject = sourceObservable
.share()
//.do((x) => console.log('#### Subject emits: ' + x))
;
let pageOneObserver;
let pageTwoObserver;
let pageThreeObserver;
setTimeout(() => {
console.log('pageOneObserver will subscribe');
pageOneObserver = subject.subscribe({
next: (x) => {
console.log('pageOneObserver gets: ' + x);
},
complete: () => {
console.log('pageOneObserver: complete');
}
});
}, 1000);
setTimeout(() => {
console.log('pageTwoObserver will subscribe');
pageTwoObserver = subject.subscribe({
next: (x) => {
console.log('pageTwoObserver gets: ' + x);
},
complete: () => {
console.log('pageTwoObserver: complete');
}
});
}, 4000);
setTimeout(() => {
console.log('pageOneObserver will unsubscribe');
pageOneObserver.unsubscribe();
}, 7000);
setTimeout(() => {
console.log('pageTwoObserver will unsubscribe');
pageTwoObserver.unsubscribe();
}, 10000);
setTimeout(() => {
console.log('pageThreeObserver will subscribe');
pageThreeObserver = subject.subscribe({
next: (x) => {
console.log('pageThreeObserver gets: ' + x);
},
complete: () => {
console.log('pageThreeObserver: complete');
}
});
}, 13000);
setTimeout(() => {
console.log('pageThreeObserver will unsubscribe');
pageThreeObserver.unsubscribe();
}, 16000);
<script src="https://unpkg.com/rxjs@5.1.1/bundles/Rx.min.js"></script>
There are some shorthand ways of creating subjects. For example:
sourceObservable.share();
// is the same as
sourceObservable.publish().refCount();
sourceObservable.publish().refCount();
// is the same as
sourceObservable.multicast(new Rx.Subject()).refCount();
sourceObservable.publishReplay().refCount();
// is the same as
sourceObservable.multicast(new Rx.ReplaySubject(1)).refCount();
sourceObservable.publishBehavior().refCount();
// is the same as
sourceObservable.multicast(new Rx.BehaviorSubject(0)).refCount();
sourceObservable.publishLast().refCount();
// is the same as
sourceObservable.multicast(new Rx.AsyncSubject()).refCount();
sourceObservable.share();
has also built in subject factory which means when the source observable is complete at some point we have to create a new instance of the sourceObservable but that can be done with only a new instance of the subject of your choice. Using the other available subjects below we have to explicitly return a factory function to the multicast operator.
When you would like to use other subject types besides the Rx.Subject() and wanting to make your observable subscription truly reusable you have to use subject factories (which is just a function that return a new instance of any subject you like to use) which is illustrated below:
const {
Observable
} = Rx;
let sourceObservable = Observable
.create((observer) => {
let count = 0;
let interval = setInterval(() => {
observer.next(count++)
}, 700);
setTimeout(() => {
clearInterval(interval);
observer.complete();
}, 5500);
return () => {
clearInterval(interval);
console.log('######## Source observable unsubscribed');
}
})
.do((x) => console.log('#### Source emits: ' + x));
/* You could return whatever subject instance you like here */
let subjectFactory = () => new Rx.ReplaySubject(1);
let subject = sourceObservable
.multicast(subjectFactory)
.refCount();
//.do((x) => console.log('#### Subject emits: ' + x))
;
let pageOneObserver;
let pageTwoObserver;
let pageThreeObserver;
setTimeout(() => {
console.log('pageOneObserver will subscribe');
pageOneObserver = subject.subscribe({
next: (x) => {
console.log('pageOneObserver gets: ' + x);
},
complete: () => {
console.log('pageOneObserver: complete');
}
});
}, 1000);
setTimeout(() => {
console.log('pageTwoObserver will subscribe');
pageTwoObserver = subject.subscribe({
next: (x) => {
console.log('pageTwoObserver gets: ' + x);
},
complete: () => {
console.log('pageTwoObserver: complete');
}
});
}, 4000);
setTimeout(() => {
console.log('pageOneObserver will unsubscribe');
pageOneObserver.unsubscribe();
}, 7000);
setTimeout(() => {
console.log('pageTwoObserver will unsubscribe');
pageTwoObserver.unsubscribe();
}, 10000);
setTimeout(() => {
console.log('pageThreeObserver will subscribe');
pageThreeObserver = subject.subscribe({
next: (x) => {
console.log('pageThreeObserver gets: ' + x);
},
complete: () => {
console.log('pageThreeObserver: complete');
}
});
}, 13000);
setTimeout(() => {
console.log('pageThreeObserver will unsubscribe');
pageThreeObserver.unsubscribe();
}, 16000);
<script src="https://unpkg.com/rxjs@5.1.1/bundles/Rx.min.js"></script>
Feel free to ask anything if there is still something unclear.