141 lines
3.4 KiB
JavaScript
141 lines
3.4 KiB
JavaScript
|
const LRU = require('lru-cache');
|
||
|
const _ = require('lodash');
|
||
|
const lru_params = [ 'max', 'maxAge', 'length', 'dispose', 'stale' ];
|
||
|
const deepFreeze = require('./lib/freeze');
|
||
|
const vfs = require('very-fast-args');
|
||
|
|
||
|
module.exports = function (options) {
|
||
|
const cache = new LRU(_.pick(options, lru_params));
|
||
|
const load = options.load;
|
||
|
const hash = options.hash;
|
||
|
const bypass = options.bypass;
|
||
|
const itemMaxAge = options.itemMaxAge;
|
||
|
const freeze = options.freeze;
|
||
|
const clone = options.clone;
|
||
|
const loading = new Map();
|
||
|
|
||
|
if (options.disable) {
|
||
|
_.extend(load, { del }, options);
|
||
|
return load;
|
||
|
}
|
||
|
|
||
|
function del() {
|
||
|
const key = hash.apply(this, arguments);
|
||
|
cache.del(key);
|
||
|
}
|
||
|
|
||
|
const result = function () {
|
||
|
const args = vfs.apply(null, arguments);
|
||
|
const parameters = args.slice(0, -1);
|
||
|
const callback = args.slice(-1).pop();
|
||
|
const self = this;
|
||
|
|
||
|
var key;
|
||
|
|
||
|
if (bypass && bypass.apply(self, parameters)) {
|
||
|
return load.apply(self, args);
|
||
|
}
|
||
|
|
||
|
if (parameters.length === 0 && !hash) {
|
||
|
//the load function only receives callback.
|
||
|
key = '_';
|
||
|
} else {
|
||
|
key = hash.apply(self, parameters);
|
||
|
}
|
||
|
|
||
|
var fromCache = cache.get(key);
|
||
|
|
||
|
if (fromCache) {
|
||
|
if (clone) {
|
||
|
return callback.apply(null, [null].concat(fromCache).map(_.cloneDeep));
|
||
|
}
|
||
|
return callback.apply(null, [null].concat(fromCache));
|
||
|
}
|
||
|
|
||
|
if (!loading.get(key)) {
|
||
|
loading.set(key, []);
|
||
|
|
||
|
load.apply(self, parameters.concat(function (err) {
|
||
|
const args = vfs.apply(null, arguments);
|
||
|
|
||
|
//we store the result only if the load didn't fail.
|
||
|
if (!err) {
|
||
|
const result = args.slice(1);
|
||
|
if (freeze) {
|
||
|
args.forEach(deepFreeze);
|
||
|
}
|
||
|
if (itemMaxAge) {
|
||
|
cache.set(key, result, itemMaxAge.apply(self, parameters.concat(result)));
|
||
|
} else {
|
||
|
cache.set(key, result);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//immediately call every other callback waiting
|
||
|
const waiting = loading.get(key).concat(callback);
|
||
|
loading.delete(key);
|
||
|
waiting.forEach(function (callback) {
|
||
|
if (clone) {
|
||
|
return callback.apply(null, args.map(_.cloneDeep));
|
||
|
}
|
||
|
callback.apply(null, args);
|
||
|
});
|
||
|
/////////
|
||
|
|
||
|
}));
|
||
|
} else {
|
||
|
loading.get(key).push(callback);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
result.keys = cache.keys.bind(cache);
|
||
|
|
||
|
_.extend(result, { del }, options);
|
||
|
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
|
||
|
module.exports.sync = function (options) {
|
||
|
const cache = new LRU(_.pick(options, lru_params));
|
||
|
const load = options.load;
|
||
|
const hash = options.hash;
|
||
|
const disable = options.disable;
|
||
|
const bypass = options.bypass;
|
||
|
const self = this;
|
||
|
const itemMaxAge = options.itemMaxAge;
|
||
|
|
||
|
if (disable) {
|
||
|
return load;
|
||
|
}
|
||
|
|
||
|
const result = function () {
|
||
|
var args = _.toArray(arguments);
|
||
|
|
||
|
if (bypass && bypass.apply(self, arguments)) {
|
||
|
return load.apply(self, arguments);
|
||
|
}
|
||
|
|
||
|
var key = hash.apply(self, args);
|
||
|
|
||
|
var fromCache = cache.get(key);
|
||
|
|
||
|
if (fromCache) {
|
||
|
return fromCache;
|
||
|
}
|
||
|
|
||
|
const result = load.apply(self, args);
|
||
|
if (itemMaxAge) {
|
||
|
cache.set(key, result, itemMaxAge.apply(self, args.concat([ result ])));
|
||
|
} else {
|
||
|
cache.set(key, result);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
result.keys = cache.keys.bind(cache);
|
||
|
|
||
|
return result;
|
||
|
};
|