There have been multiple discussions on caching http-requests
with RXJS. In this question/proposal I want to propose a custom rxjs-operator (non-pure) to provide caching:
const cacheHttp = (cacheKey: string, cacheStorage: any) => (source: Observable<any>) => {
if (!cacheStorage[cacheKey]) {
cacheStorage[cacheKey] = source.pipe(
shareReplay(1)
);
}
return cacheStorage[cacheKey];
};
This operator is not pure, as it modifies one of its arguments (cacheStorage
).
This operator could be used like this:
public cachedItems = {};
public getDataForItem$(itemId: string) {
return this.http.get('/item/' + itemId).pipe(
cacheHttp(itemId, this.cachedItems),
shareReplay(1)
);
}
The client could then call this multiple times without causing superfluous http-requests
:
// the following two subscriptions cause http-requests
this.itemService.getDataForItem('firstItem').subscribe((val) => console.log(val));
this.itemService.getDataForItem('secondItem').subscribe((val) => console.log(val));
// all further subscriptions would not cause any additional http-requests
this.itemService.getDataForItem('firstItem').subscribe((val) => console.log(val));
this.itemService.getDataForItem('secondItem').subscribe((val) => console.log(val));
this.itemService.getDataForItem('firstItem').subscribe((val) => console.log(val));
this.itemService.getDataForItem('secondItem').subscribe((val) => console.log(val));
// this subscription would again cause an http-request:
this.itemService.getDataForItem('thirdItem').subscribe((val) => console.log(val));
Is this an acceptable approach to solving the "cache-for-different-requests-problem"? Is there possibly a memory-leak or are there any leaked subscriptions? Is it ok to have side-effects on a provided argument?