@web-atoms/core
Version:
107 lines (89 loc) • 2.49 kB
text/typescript
import { App } from "../App";
import { Atom } from "../Atom";
import DISingleton from "../di/DISingleton";
import { Inject } from "../di/Inject";
export type CacheSeconds<T> = number | ((result: T) => number);
export interface ICacheEntry<T> {
/**
* Cache Key, must be unique
*/
key: string;
/**
* Time to Live in seconds, after given ttl
* object will be removed from cache
*/
ttlSeconds?: CacheSeconds<T>;
// /**
// * Not supported yet
// */
// expires?: Date;
/**
* Cached value
*/
value?: Promise<any>;
}
interface IFinalCacheEntry extends ICacheEntry<any> {
finalTTL?: number;
timeout?: any;
}
()
export default class CacheService {
private cache: { [key: string]: IFinalCacheEntry } = {};
constructor( private app: App) {
}
public remove(key: string): any {
const v = this.cache[key];
if (v) {
this.clear(v);
return v.value;
}
return null;
}
public async getOrCreate<T>(
key: string,
task: (cacheEntry?: ICacheEntry<T>) => Promise<T> ): Promise<T> {
const c = this.cache[key] || (this.cache[key] = {
key,
finalTTL: 3600
});
if (!c.value) {
c.value = task(c);
}
let v: any = null;
try {
v = await c.value;
} catch (e) {
this.clear(c);
throw e;
}
if (c.ttlSeconds !== undefined) {
if (typeof c.ttlSeconds === "number") {
c.finalTTL = c.ttlSeconds;
} else {
c.finalTTL = c.ttlSeconds(v);
}
}
if (c.timeout) {
clearTimeout(c.timeout);
}
if (c.finalTTL) {
this.cache[key] = c;
c.timeout = setTimeout(() => {
c.timeout = 0;
this.clear(c);
}, c.finalTTL * 1000);
} else {
// this is the case where we do not want to store
this.clear(c);
}
return await c.value;
}
private clear(ci: IFinalCacheEntry): void {
if (ci.timeout) {
clearTimeout(ci.timeout);
ci.timeout = 0;
}
this.cache[ci.key] = null;
delete this.cache[ci.key];
}
}