transitory
Version:
In-memory cache with high hit rates via LFU eviction. Supports time-based expiration, automatic loading and metrics.
62 lines • 2.12 kB
JavaScript
import { WrappedCache } from '../WrappedCache';
const DATA = Symbol('loadingData');
/**
* Extension to another cache that will load items if they are not cached.
*/
export class DefaultLoadingCache extends WrappedCache {
constructor(options) {
super(options.parent, options.removalListener || null);
this[DATA] = {
promises: new Map(),
loader: options.loader || null
};
}
/**
* Get cached value or load it if not currently cached. Updates the usage
* of the key.
*
* @param key -
* key to get
* @param loader -
* optional loader to use for loading the object
* @returns
* promise that resolves to the loaded value
*/
get(key, loader) {
const currentValue = this.getIfPresent(key);
if (currentValue !== null) {
return Promise.resolve(currentValue);
}
const data = this[DATA];
// First check if we are already loading this value
let promise = data.promises.get(key);
if (promise)
return promise;
// Create the initial promise if we are not already loading
if (typeof loader !== 'undefined') {
if (typeof loader !== 'function') {
throw new Error('If loader is used it must be a function that returns a value or a Promise');
}
promise = Promise.resolve(loader(key));
}
else if (data.loader) {
promise = Promise.resolve(data.loader(key));
}
if (!promise) {
throw new Error('No way to load data for key: ' + key);
}
// Enhance with handler that will remove promise and set value if success
const resolve = () => data.promises.delete(key);
promise = promise.then(result => {
this.set(key, result);
resolve();
return result;
}).catch(err => {
resolve();
throw err;
});
data.promises.set(key, promise);
return promise;
}
}
//# sourceMappingURL=DefaultLoadingCache.js.map