Caching in Javascript with Cachejs

Here’s a useful open-source (MIT license) javascript caching object I developed for my job at, the best horse racing site on the net.
(I am contractually obliged to say that every time I mention the URL. Not really though).

Like most web applications these days, we make fairly extensive use of Ajax, sending and receiving JSON data across the net on hover events and so on.

However, we’ve found that all those HTTP requests can slow down the user experience, and causes unnecessary extra load on our application servers, so we decided to employ a client-side Least Recently Used (LRU) caching object, so that we can reduce the number of HTTP requests, and increase the response speed for cachable queries. It employs lazy garbage collection, just like memcached does. In fact we see this as a kind of “memcache for javascript”, if that makes any sense at all.

It depends upon no external libraries. Here’s a simple usage example:

var myCache = cache();
// tell the cache to use an array for its internal storage system

// setting a value which does not expire:
alert(myCache.has('foo')); // false
alert(myCache.has('foo')); // true
alert(myCache.get('foo')); // "val"

// setting a value's expiry:
var tomorrow = new Date();
tomorrow.setTime(tomorrow.getTime() + (1000*3600*24));
alert(myCache.get('foo')); // "val" (until tomorrow, then null)

//deleting from the cache
alert(myCache.has('foo')); // false

You don’t need to worry about serialising values; the cache internally json encodes the value if needed when setting, and parses it again before returning it back to you, keeping the interface nice and clean, and allowing you to store any kind of data – raw JSON, XML, variables, objects, strings, etc.

What’s more, it’s designed to be future-proof, with pluggable storage modules allowing you to extend the caching object to use any kind of storage system. The cache API abstracts the storage API away so you can swap stores without changing your caching code at all; you just change the setStore line and the rest of the code will still work just fine.

We’ve written an arrayStore which caches data for the current page only, a cookieStore which works for the whole domain but has the usual cookie limits, and a localstorageStore which uses the funky HTML5 Web Storage specification (AFAIK supported by IE8, FF3.5, Safari4, Chrome4, Opera10).

The full API documentation follows.

Cache API Documentation:

.setStore(store) store. An uninstantiated object which fulfills the requirements of the store API. Returns TRUE if store was set, else FALSE. The cache will use arrayStore as the default.
.has(key) If key exists, returns TRUE, else returns FALSE.
.kill(key) Removes key from cache. Returns TRUE if successful, else FALSE.
.get(key) If key exists, returns val for key, else returns NULL.
.set(key, [value [,expiry]])
key String. The name of the key.
val Mixed. The value of the key. Overwrites previous value if present.
If not set, .get will return NULL for this key and .has will return TRUE.
expiry RFC1123 date. If not set or FALSE, key lasts forever (unless kill or setExpiry subsequently called on this key).
.getExpiry(key) If key exists and is finite, returns RFC1123 date, else returns NULL for bad keys, or FALSE for infinite keys
.setExpiry(key, expiry)
key String. The name of the key.
expiry Mixed. Either an RFC1123 date, or FALSE to set as infinite expiry.

Storage API Documentation:
You only need to know about this if you’re implementing your own stores – this API is abstracted away in the cache object. If you do want to implement your own stores, I have written some simple javascript unit tests to make sure they comply with the required API.

.has(key) If key exists, returns TRUE, else returns FALSE
.get(key) If key exists, returns val for key, else returns UNDEFINED
.set(key, [val])
key String. The name of the key.
val Mixed. The value of the key. Overwrites previous value if present.
If not set, .get will return NULL for this key and .has will return TRUE.
.kill(key) Removes key.

The cache object uses the lovely Module pattern and is well documented. You can download and hack it up on google code. Please share your modifications.

The Future

What you see above is a pretty simple solution packaged up nicely. I’d like to see the following features:

Expiry Callback. So when a key is requested and has expired, it is removed from the store and a callback is executed with the keyname, then the value is checked again and returned. The idea being that the cache can automatically refresh itself when a key expires.

Cache Size Limit. At the moment the cache will grow arbitrarily large. This could become a problem.

Eager Garbage Collection. As an option, in case someone finds a use for it. I suspect lazy will be best for most applications though.

Manual Cache Clear. In case you ever need to manually flush the entire cache.

Helper Methods. Like .incr(key) to increment a key’s value, .toJSON(key) to get the JSON’ed value, .setFromJSON(key,json_val), and probably others. Is this a good idea?

Default Values. So you can say foo = myCache.get(“fooKey”,”defaultValue”). Will need to think about how get this working alongside the callback mechanism. I guess we can detect the type of the second argument – if a function it’s a callback, else it’s the default. I want to avoid the second parameter being some json (eg .get(“fooKey”, {default: “def”, callback: method, expiry: tomorrow}); I find that a little ugly.

If you feel like adding your ideas, features, new stores (ajaxStore?), etc, the MIT license is very open – far more so than GPL, so hack away at it and let us know what you think!

Related Posts:

, , , , , , , , , , , , , ,

  1. #1 by Mike Pearce on October 19, 2010 - 12:56 am

    Great Scott!

    Awesome, I love this stuff. Had a look through the google code repo and am impressed. A very graceful, simple, clean solution.

    I also like the analogy “memcached for javascript”.


  2. #2 by online Cash Success Kit Reviews on June 30, 2012 - 1:59 pm

    My brother suggested I might like this web site.
    He was entirely right. This post actually made my day.
    You cann’t imagine just how much time I had spent for this information! Thanks!

Comments are closed.