Created
November 23, 2017 21:37
-
-
Save gdugas/2d2c47273d79134e84c0a8e99f440591 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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