The ultimate Javascript cache module.
Unobtrusive and failsafe caching of anything.
Caches functions instead of data.
npm install hamster
var hamster = require('hamster');
// Cache a function
var getData = hamster(aFunctionThatTakesLotsOfTime);
// Call the function like you normally would
getData('yeah', resultHandler);
- Simple straight-forward API
- No need to manage a cache object yourself
- Easy to implement, no need to rewrite any function calls
- Promotes separation of concerns
- TTL-support, clear caches after a certain time
- LRU-support, pop the least recently updated cache when reaching a max number of cached results
- Pontentially supports any cache storage (for example redis)
When you cache a function using hamster(fn)
, a function is returned. You
can use this function in exactly the same way as the original. No other part of
your code should ever have to care about that the function is cached.
Also the cached function itself should not and need not be modified with caching in mind. The function should do what it does, callers should do what they do, while Hamster automagically takes care of the cache.
Note
Do not use Hamster with functions that are based on side-effects. That is, if it modifies an argument object or doesn't return the result, or if the result varies depending on non-argument variables (eg global vars or properties on its object).
A typical example of this would be trying to cache an express/connect middleware directly. That won't work since a middleware just modifies an object, that also is unique to every request. Instead, rewrite the function to only require what arguments it needs. For example:
// Do not:
app.use(hamster(getUserData));
// Do:
getUserData = hamster(function(id){...});
app.use(function (req, res, next) {
getUserData(req.params.id, function (err, result) {
req.user = result;
next();
});
});
npm install hamster
See releases for browserified source files.
A global instance of Hamster (see hamster() below).
Creates a new instance of hamster with some customizable options (see options below).
An instance of Hamster, managing cached functions. Call it with a function to create a cache for that function. Returns a function that should replace the original function.
A shorthand method for calling hamster(fn, { async: false })
.
A shorthand method for calling hamster(fn, { async: true })
.
Just use it as you would use the original function.
A reference to the original function, if you need to circumvent the cache.
Clear all cached results (no key) or the result for a specific set of arguments.
{
async: true, // Set to false to enable caching synchronous functions
ttl: 10000, // How long to wait before the cached result is cleared, in milliseconds
maxSize: 50, // Max number of cached results, the least recently used will be dropped when exeeding this limit
keys: [] // Possibility to create a new cache only for certain arguments/properties
}
A note on options.keys. Let's say you supply the entire req object but for caching reasons the funciton is only interested in req.params.id. Then the following would consider requests to be the same if anything but req.params.id changes.
// 0 for the first argument
cache = hamster(fn, { keys: ['0.params.id'] });
// Will only request a post from the server the first time
var getPost = hamster(function (id, callback) {
$.ajax({
url: '/posts/' + id,
success: callback
});
}, {
ttl: 0
});
$('.toc a').on('click', function(e) {
e.preventDefault();
getPost($(this).attr('data-id'), function (data) {
renderPost(data);
});
});
postSchema.statics.getFull = hamster(function (id, callback) {
this.findById(id).populate('author').exec(callback);
});
// ... used in routing like normal:
app.get('/:id', function (req, res) {
postModel.getFull(req.params.id, function (post) {
res.render('post', post);
});
});
See the test folder for more examples.
Want to store the caches in redis or localStorage instead of in-memory? Go ahead and implement .store, .hasCache, .getCache, .addTimeout, and possibly any other methods on a cache instance. I will probably implement a neater api for this later.
Create an issue first so I know what you're doing. Pull request to the develop branch. Test everything.
AKA implemented when/if needed.
- A way of expiring based on some other criteria than time. Eg number of calls or some external change. Probably in the form of a .expireWhen() option.
- A way to auto-update a cache instead of exipring. A must for very heavy work which not even one client can wait for.
- API for using custom storage mechanisms.
MIT
Copyright (C) 2013 Andreas Hultgren