Skip to content

Instantly share code, notes, and snippets.

@gdugas
Created November 23, 2017 21:37
Show Gist options
  • Save gdugas/2d2c47273d79134e84c0a8e99f440591 to your computer and use it in GitHub Desktop.
Save gdugas/2d2c47273d79134e84c0a8e99f440591 to your computer and use it in GitHub Desktop.
import { Observable, Subject } from 'rxjs';
export class CacheEntryOptions {
expire?: number;
updateIf?: Function;
}
export class CacheEntry<T> {
private value: T;
private context: any;
constructor(context?: any) {
this.setContext(context || {});
}
set(value: T) {
this.value = value;
}
get(): T {
return this.value;
}
setContext(context: any) {
this.context = context;
}
getContext(): any {
return this.context;
}
}
export class Cache {
entries: Map<string, CacheEntry<any>> = new Map<string, CacheEntry<any>>();
onExpire: Subject<[string, CacheEntry<any>]> = new Subject<[string, CacheEntry<any>]>();
/**
* Resolved with CacheEntry only if key exists.
*
* @param key
*/
get(key: string): Promise<CacheEntry<any>> {
return new Promise<any>((resolve, reject) => {
if (this.entries.has(key)) {
resolve(this.entries.get(key));
} else {
reject();
}
});
}
/**
* Set a value.
*
* @param key
* @param value
* @param options
*/
set(key: string, value: string, options?: CacheEntryOptions): Promise<CacheEntry<any>> {
const _options: CacheEntryOptions = options || new CacheEntryOptions();
return new Promise((resolve, reject) => {
this.get(key).then((entry: CacheEntry<any>) => {
if (!this.canUpdate(entry, _options)) {
reject(entry);
} else {
this.updateEntry(key, entry, value, _options) && resolve(entry);
}
}).catch(() => {
resolve(this.createEntry(key, value, _options));
});
});
}
private canUpdate(entry: CacheEntry<any>, options: CacheEntryOptions) {
return !options.updateIf || options.updateIf(entry.getContext());
}
private createEntry(key: string, value: any, options: CacheEntryOptions): CacheEntry<any> {
const entry = new CacheEntry<any>();
entry.set(value);
this.entries.set(key, entry);
this.triggerExpiration(key, entry, options);
return entry;
}
private updateEntry(key: string, entry: CacheEntry<any>, value: any, options: CacheEntryOptions) {
this.clearExpiration(entry);
entry.set(value);
this.triggerExpiration(key, entry, options);
}
private clearExpiration(entry: CacheEntry<any>): void {
const expirationId = entry.getContext().expirationId;
expirationId && clearTimeout(expirationId);
}
private triggerExpiration(key: string, entry: CacheEntry<any>, options: CacheEntryOptions): any {
const expire = options.expire || 0;
if (expire <= 0) {
return;
}
entry.getContext().expirationId = setTimeout(() => this.triggerOnExpire(key, entry), expire);
}
private triggerOnExpire(key: string, entry: CacheEntry<any>) {
this.entries.delete(key);
this.onExpire.next([key, entry]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment