I am currently working on an Angular 4 application that serves as a dashboard for a system. Several different components within the application make calls to the same REST endpoint using identical TypeScript service classes. While this setup functions correctly, I am looking to prevent unnecessary duplicate requests from overwhelming the server by introducing a caching mechanism on the client side.
To address this, I have developed a caching solution in TypeScript which is utilized by my services. The services pass the HTTP call into a computeFunction
:
@Injectable()
export class CacheService {
private cacheMap = {};
getAsObservable<V>(
key: string,
expirationThresholdSeconds: number,
computeFunction: () => Observable<V>): Observable<V> {
const cacheEntry = this.cacheMap[key];
if (...) {
return Observable.of<V>(cacheEntry.value);
} else {
return computeFunction().map(returnValue => {
const expirationTime = new Date().getTime() + (expirationThresholdSeconds * 1000);
const newCacheEntry = ... // build cache entry with expiration set
this.cacheMap[key] = newCacheEntry;
return returnValue;
});
}
}
While this approach works as intended, I have noticed that consecutive calls made with the same key
can lead to multiple server requests as the cache may not yet contain the return value at the time of verification.
Therefore, I believe it might be beneficial to create a custom cacheable wrapper "multiplexing" Observable
that can be shared among multiple subscribers:
- Execute the
computeFunction
only once - Cache the returned value
- Distribute the cached value to all subscribers and manage cleanup like regular HTTP
Observable
s do
I would appreciate it greatly if someone could provide me with a sample implementation of this concept.
The primary challenge lies in ensuring that the Observable
can handle scenarios where:
- Subscriptions are established before the wrapped
computeFunction
returns (waiting until the subscription occurs) - Subscriptions are initiated after the wrapped
computeFunction
has already returned (providing the cached value)
Am I potentially overcomplicating this process? If there is a simpler approach that I should consider, I am eager to learn about it.