Skip to content

Instantly share code, notes, and snippets.

@lesleh
Last active December 1, 2023 12:21
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save lesleh/9d66bc87f22bf5e28997 to your computer and use it in GitHub Desktop.
Save lesleh/9d66bc87f22bf5e28997 to your computer and use it in GitHub Desktop.
Basic memory cache implementation for JavaScript.
function Cache(config) {
config = config || {};
config.trim = config.trim || 600;
config.ttl = config.ttl || 3600;
var data = {};
var self = this;
var now = function() {
return new Date().getTime() / 1000;
};
/**
* Object for holding a value and an expiration time
* @param expires the expiry time as a UNIX timestamp
* @param value the value of the cache entry
* @constructor ¯\(°_o)/¯
*/
var CacheEntry = function(expires, value) {
this.expires = expires;
this.value = value;
};
/**
* Creates a new cache entry with the current time + ttl as the expiry.
* @param value the value to set in the entry
* @returns {CacheEntry} the cache entry object
*/
CacheEntry.create = function(value) {
return new CacheEntry(now() + config.ttl, value);
};
/**
* Returns an Array of all currently set keys.
* @returns {Array} cache keys
*/
this.keys = function() {
var keys = [];
for(var key in data)
if (data.hasOwnProperty(key))
keys.push(key);
return keys;
};
/**
* Checks if a key is currently set in the cache.
* @param key the key to look for
* @returns {boolean} true if set, false otherwise
*/
this.has = function(key) {
return data.hasOwnProperty(key);
};
/**
* Clears all cache entries.
*/
this.clear = function() {
for(var key in data)
if (data.hasOwnProperty(key))
self.remove(key);
};
/**
* Gets the cache entry for the given key.
* @param key the cache key
* @returns {*} the cache entry if set, or undefined otherwise
*/
this.get = function(key) {
return data[key].value;
};
/**
* Returns the cache entry if set, or a default value otherwise.
* @param key the key to retrieve
* @param def the default value to return if unset
* @returns {*} the cache entry if set, or the default value provided.
*/
this.getOrDefault = function(key, def) {
return self.has(key) ? data[key].value : def;
};
/**
* Sets a cache entry with the provided key and value.
* @param key the key to set
* @param value the value to set
*/
this.set = function(key, value) {
data[key] = CacheEntry.create(value);
};
/**
* Removes the cache entry for the given key.
* @param key the key to remove
*/
this.remove = function(key) {
delete data[key];
};
/**
* Checks if the cache entry has expired.
* @param entrytime the cache entry expiry time
* @param curr (optional) the current time
* @returns {boolean} true if expired, false otherwise
*/
this.expired = function(entrytime, curr) {
if(!curr)
curr = now();
return entrytime < curr;
};
/**
* Trims the cache of expired keys. This function is run periodically (see config.ttl).
*/
this.trim = function() {
var curr = now();
for (var key in data)
if (data.hasOwnProperty(key))
if(self.expired(data.expires, curr))
self.remove(key);
};
// Periodical cleanup
setInterval(this.trim, config['trim'] * 1000);
//--------------------------------------------------------
// Events
var eventCallbacks = {};
this.on = function(event, callback) {
// TODO handle event callbacks
};
}
module.exports = Cache;
@tedsteen
Copy link

tedsteen commented Jun 3, 2022

Nice simple solution, but there is a small mistake, line 118 should be if(self.expired(data[key].expires, curr))

@tedsteen
Copy link

tedsteen commented Jun 3, 2022

Also here's a pretty nice and common use case

    this.getOrCreate = function(key, fn) {
        if (self.has(key)) {
            return self.get(key);
        }
        const value = fn();
        self.set(key, value);
        return value;
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment