September 2020

Simple in-memory javascript cache

This is a simple in-memory cache. I would recommend using this pattern for most use cases:

const data = await getSetCache('key', Infinity, async () => {
  const data = await fetch('https://remoteAPI');
  return data;
})

getSetCache(key, maxAge, promiseFunction) will return a cached value if it's available and it's age is less than the 2nd argument (in milliseconds). Otherwise it will call the promiseFunction, cache the resulting value before resolving with that value.

The available commands are:

  • getCache(key)
  • setCache(key, value)
  • deleteCache(key)
  • getCacheAge(key)
  • getSetCache(key, maxAge, promiseFunction)

Full Code:

const cache = {'__timestamps__': {}}

function getCache(key) {
  return cache[key];
}

function setCache(key, value) {
  cache[key] = value;
  cache['__timestamps__'][key] = new Date().getTime();
}
function getCacheAge(key) {
  // Returns cache age, otherwise infinity
  const timestamp = cache['__timestamps__'][key];
  if (timestamp !== null) {
    return (new Date().getTime() - timestamp);
  } else {
    return Infinity;
  }
}

function getSetCache(key, maxAge, cacheMissPromise, promiseArgs) {
  // Will get the cache if it's younger than maxAge. Will call the Promise function and store/resolve with the resulting value.
  // If maxAge <= 0, will default to no caching. 
  // If maxAge === Infinity, cache will never expire
  return new Promise((resolve, reject) => {
    const cacheAge = getCacheAge(key);
    if (cacheAge < maxAge) {
      resolve(getCache(key));
    } else {
      cacheMissPromise.apply(this, promiseArgs).then((value) => {
        if (maxAge > 0) {
          setCache(key, value);
        }
        resolve(value);
      }).catch((e) => {
        reject(e);
      });
    }
  });
}

function deleteCache(key) {
  const timestamps = cache['__timestamps__'];
  delete timestamps[key];
  delete cache[key];
}

Run these commands in the browser console to see the contents of the cache:

> setCache('key', 'Valu3')
> console.log(cache);
{
    __timestamps__: {
        key: 1600393026009 
    },
    key: 'Valu3'
}

This cache can easily be swapped for calls a proper cache like redis when your backend needs to scale. There is no garbage collection, so to avoid memory leaks its recommend to use explicitly named keys.