Skip to content

Instantly share code, notes, and snippets.

@usefulthink
Created August 16, 2013 11:14
Show Gist options
  • Save usefulthink/6249058 to your computer and use it in GitHub Desktop.
Save usefulthink/6249058 to your computer and use it in GitHub Desktop.
an async general-purpose caching implementation that can be wrapped around arbitrary async functions.
module.exports = cache;
/**
* creates a cache around the given async function.
*
* @example
* function waitAndResolve(time, value, callback) {
* setTimeout(function() {
* callback(null, value);
* }, time);
* }
*
* var cachedWaitAndResolve = cache(waitAndResolve, {
* // our hashing-function should only use the value as cache-key
* hasher: function(args) { return args[1]; }
* });
*
* console.log('[1] > "foo"');
* cachedWaitAndResolve(1000, 'foo', function(err, result) {
* console.log('[1] < "'+result+'"');
* });
*
* // this will actually resolve after 1s since the first response becomes available by then…
* console.log('[2] > "foo"');
* cachedWaitAndResolve(2000, 'foo', function(err, result) {
* console.log('[2] < "'+result+'"');
* });
*
*
* @param {function} fn an async function, something like function(args..., callback) {}
* @param {Object} [options]
* @option {function(array) : string} hasher
*/
function cache(fn, options) {
options = options || {};
var hasher = options.hasher || JSON.stringify;
wrapper.cache = {};
function wrapper() {
// take the original functions arguments and remove the callback
var args = Array.prototype.slice.call(arguments),
callback = args.pop(),
cacheKey = hasher(args),
_cache = wrapper.cache[cacheKey] = wrapper.cache[cacheKey] || {};
// -- already have a completed response
if(_cache.complete) {
// programs might expect the function to be strictly async
process.nextTick(function() {
callback.apply(_cache.scope, _cache.result);
});
return;
}
// -- pending request: add callback to notification-queue
if(_cache.pending) {
_cache.onComplete.push(callback);
return;
}
// -- create or add to the list of callbacks for this entry
(_cache.onComplete = _cache.onComplete || []).push(callback);
// -- caching: rewrite the callback so that it calls all onComplete-functions in order
_cache.callback = function() {
var callbacks = _cache.onComplete;
_cache.pending = false;
_cache.complete = true;
_cache.scope = this;
_cache.result = Array.prototype.slice.call(arguments);
while(callbacks.length > 0) { callbacks.shift().apply(this, arguments); }
};
// -- set request as pending
_cache.pending = true;
// -- call the original function with our new callback
args.push(_cache.callback);
fn.apply(this, args);
}
return wrapper;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment