Last active
August 27, 2017 21:15
-
-
Save ThomasBurleson/c8fce5fcedd92dcc9102 to your computer and use it in GitHub Desktop.
Memoization functions with timeouts and call-once options...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;(function(root) { | |
'use strict'; | |
// A 32-bit checksum of a string. | |
if(!String.prototype.checksum) { | |
String.prototype.checksum = checksum; | |
} | |
// Publish the functions to the `root` package | |
root.memoize = memoize; | |
root.timed = timed; | |
root.once = once; | |
root.timedOnce = timedOnce; | |
// ****************************************************** | |
// Memoized Functions | |
// ****************************************************** | |
/** | |
* | |
*/ | |
function memoize(f) { | |
validateFunction( f ); | |
/* | |
The cache object for the memoized function. Each function | |
will hold its own cache object. The object will be indexed | |
by 32-bit checksums of argument-lists. | |
*/ | |
var cache = {}; | |
return function MemoizedFunction() { | |
// Create a checksum of all the arguments, if any argument is available. | |
var checksum = calculateChecksum(arguments); | |
// if the checksum is unknown in the cache, call the function and cache it. | |
if ( !hasCheckSum(cache,checksum) } | |
cache[checksum] = f.apply(f, arguments); | |
} | |
// return the cached value. | |
return cache[checksum]; | |
}; | |
}; | |
/** | |
* | |
*/ | |
function timed(f, timeout) { | |
/* | |
The cache object for the memoized function. Each function | |
will hold its own cache object. The object will be indexed | |
by 32-bit checksums of argument-lists. | |
The times object maintains a list of 'last updated time'. | |
These times are used to determine the age of the cache | |
item. | |
*/ | |
var cache = {}; | |
var times = {}; | |
validateFunction( f ); | |
validateTimeout( timeout ); | |
return function TimedMemoizedFunction() { | |
// What's the current time? | |
var now = +new Date(); | |
var checksum = calculateChecksum(arguments); | |
/* | |
if the checksum is unknown in the cache, call the function and cache it. | |
if the last update time is unknown in the times cache, call the function and cache it. | |
if the current time minus the last update time is equal to or greater than the timeout, | |
call the function and cache it. | |
*/ | |
if ( !hasCheckSum(cache,checksum) || | |
!hasCheckSum(times,checksum) || | |
(now - times[checksum]) >= timeout ) { | |
cache[checksum] = f.apply(f, arguments); | |
times[checksum] = now; | |
} | |
// return the cached value. | |
return cache[checksum]; | |
}; | |
}; | |
/** | |
* Once is a function which allows a function to be called only once, | |
* regardless of the input. | |
*/ | |
function once(f) { | |
var cached; | |
validateFunction( f ); | |
return function OnceFunction() { | |
if(typeof(cached) === 'undefined') { | |
cached = f.apply(f, arguments); | |
} | |
return cached; | |
}; | |
}; | |
/** | |
* | |
*/ | |
function timedOnce(f, timeout) { | |
var cached; | |
var lut = 0; | |
validateFunction( f ); | |
validateTimeout( timeout ); | |
return function TimedOnceFunction() { | |
var now = +new Date(); | |
if(typeof(cached) === 'undefined' || (now - lut) >= timeout) { | |
cached = f.apply(f, arguments); | |
lut = now; | |
} | |
return cached; | |
}; | |
}; | |
// ****************************************************** | |
// Internal Helper Functions | |
// ****************************************************** | |
/** | |
* | |
*/ | |
function hasCheckSum(cache, checksum) { | |
return (typeof(cache[checksum]) !== 'undefined'); | |
} | |
/** | |
* Build checksum value representing the String's value | |
*/ | |
function checkSum() { | |
var checksum = 0, i, chr, len; | |
if (this.length == 0) { | |
return checksum; | |
} | |
for (i = 0, len = this.length; i < len; i++) { | |
checksum = ((((checksum << 5) - checksum) + this.charCodeAt(i)) | 0); | |
} | |
return checksum; | |
}; | |
/** | |
* Confirm that the argument is a Function reference | |
*/ | |
function validateFunction(f) { | |
if(typeof(f) !== "function" || !(f instanceof Function)) { | |
throw new TypeError("Provided argument is not a function."); | |
} | |
} | |
/** | |
* Confirm that the variable is a number | |
*/ | |
function validateTimeout(timeout) { | |
if(typeof(timeout) !== "number") { | |
throw new TypeError("Provided argument is not a number."); | |
} | |
} | |
/** | |
* For the specified Function arguments, calculate the checksum to lookup | |
* previous invocations of the Function. | |
*/ | |
function calculateChecksum(args) { | |
// Create a checksum of all the arguments, if any argument is available. | |
var checksum = 0; | |
// Convert to array for .call() methods... | |
args = Array.prototype.slice.call(args); | |
if(args.length > 0) { | |
// if you don't expect complex argument structure, this will suffice | |
// checksum = Array.prototype.join.call(args, ",").checksum(); | |
// serialize the arguments object to a JSON string and create a 32-bits checksum. | |
checksum = JSON.stringify(arguments).checksum(); | |
} | |
return checksum; | |
}; | |
}('mem' in window ? window.mem : window.mem = {})); | |
I also love the trick in the Module Wrapper (aka IIFE):
}('mem' in window ? window.mem : window.mem = {}));
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Based on article @http://codepen.io/ImagineProgramming/storydump/javascript-memoization-timeout