I'm implementing the cache using BehaviorSubject and multicast. The stream returned from the cache should start with HTTP request. I should also be able to force refresh the cache by triggering manually next on subject. The common approach with two subjects outlined by Poul Kruijt is well known and suggested everywhere. My idea is to find a way to achieve the following using only one subject throughout lifecycle of a stream.
It would be easy to achieve with multicast like this
const cache = new BehaviorSubject(null);
const shared = queryThatCompletes.pipe(multicast(cache)) as any;
// sets up subscription, waits for connect
shared.subscribe((values) => console.log(values));
// triggers http request
shared.connect();
setTimeout(() => {
// will only emit COMPLETE from subject
shared.subscribe((values) => console.log(values));
}, 2000);
// force refresh the cache
cache.next();
but since the HTTP query stream completes, the second subscription doesn't get any value, just COMPLETE notification from subject. This behavior is described in detail here.
The other option is to pass a factory function instead of the subject instance like this:
const cache = ()=> new BehaviorSubject(null);
const shared = queryThatCompletes.pipe(multicast(cache)) as any;
This will re-create the subject, that will subscribe to queryThatCompletes and re-trigger HTTP request. But the downsides is the need to call connect multiple times and redundant queries.
const cache = () => new BehaviorSubject(null);
const shared = queryThatCompletes.pipe(multicast(cache)) as any;
// sets up subscription, waits for connect
shared.subscribe((values) => console.log(values));
// triggers http request
shared.connect();
setTimeout(() => {
// sets up subscription, waits for connect
shared.subscribe((values) => console.log(values));
// triggers http request
shared.connect();
}, 2000);
So I simply implemented HTTP stream that doesn't complete by itself and use it like this:
const queryOnceButDontComplete = new Observable((observer) => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(data => observer.next(data));
return () => {};
});
const cache = new BehaviorSubject(null);
const shared = queryOnceButDontComplete.pipe(multicast(cache)) as any;
// sets up subscription, waits for connect
shared.subscribe((values) => console.log(values));
// triggers http request
shared.connect();
setTimeout(() => {
// sets up subscription, waits for connect
shared.subscribe((values) => console.log(values));
}, 2000);
This works, but I'm wondering if there's a way to achieve what I want without the use of custom observable. Any ideas?