Could you please clarify what is going on here and whether this is a legitimate approach?
I have a method for fetching data. When data is fetched, I want to store it in a BehaviorSubject:
test$ = new BehaviorSubject<any[] | undefined>(undefined);
async test2$(): Promise<any[] | undefined> {
//...network request...
this.test$.next([2]);
return [2];
}
However, I am aware that the cached data may not have been fetched yet, so I must first check for the default value – undefined
:
this.test$
.pipe(
concatMap((n) =>
n?.length ? of(n) : from(this.test2$()).pipe(switchMap(() => this.test$))
)
)
.subscribe((n) => {
console.log('subscribe n', n);
});
What I’m afraid of is an endless loop, which is exactly what happens without this line pipe(switchMap(() => this.test$))
, but I’m not sure how the switchMap
helps here.
I believe the pipe eventually resolves to this.test$
again, and all emissions to concatMap
, except the first, are cancelled?
1
Take this approach instead :
private cache = new BehaviorSubject<any>(undefined);
public cache$ = this.cache.asObservable().pipe(
filter(Boolean);
);
loadData() {
if (this.cache.value) return this.cache$.pipe(first());
else return this.fetchData();
}
private fetchData() {
return this.http.get('...').pipe(
tap(res => this.cache.next(res))
);
}
This will load the data if it’s not present, otherwise it will return the cache, and either way, your subscription will not work unless there is data in the cache (thanks to the filter
operator)
4
Looks too complicated, you can convert the promise to an observable using from
and then use share
to cache the value. There is no need for the behaviour subject.
test$ = new BehaviorSubject<any[] | undefined>(undefined);
test2$(): Observable<any> {
return from(/* enter promise here */).pipe(
share(),
);
}
If you want to implement a cache, then you can also try AsyncSubject.
Article on Async Subject
const cache = {};
function getResource(url) {
if (!cache[url]) {
cache[url] = new AsyncSubject();
fetch(url)
.then((response) => response.json())
.then((data) => {
cache[url].next(data);
cache[url].complete();
});
}
return cache[url].asObservable();
}
const url = 'https://api.mocki.io/v1/ce5f60e2';
getResource(url).subscribe((data) => console.log(data));
setTimeout(() => {
// no request is made, data is served from the AsyncSubject's cache
getResource(url).subscribe((data) => console.log(data));
}, 3000);
1