Function caching in Javascript (caching != catching)
Today we are going to dive into Function Caching in Javascript. Read that again: C-A-C-H-I-N-G. Not catching, from try
and catch
, as I think there are enough resources in the web that cover that topic. We are going to see Caching, from Cache, like when your browser stores content previously loaded.
What's Function Caching?
Function Caching, or just Caching means the use of memoization in order to save processing time in our programs. Usually, this means that we have some sort of data structure that saves previous results so we don't compute them again. Since Javascript's functions behave like objects, we can use the functions themselves instead of external resources.
Example [1]
function isPrime( num ) {
if ( isPrime.cache[ num ] != null )
return isPrime.cache[ num ];
var prime = num != 1; // Everything but 1 can be prime
for ( var i = 2; i < num; i++ ) {
if ( num % i == 0 ) {
prime = false;
break;
}
}
isPrime.cache[ num ] = prime
return prime;
}
isPrime.cache = {};
The catch is (pun intended) that functions can have properties as objects. In our case, we add a cache
property which is an Object. An Object, due its nature in Javascript, can work as a Hash Table (actually more like a map or dictionary, but you get the drill) and thus work render the function of a Cache data structure.
Usage of this feature
-
Fetching data from a server: Most of the time you have a function that retrieves information from a server. If you have a cache copy of it, you don't need to hit the server again. You could also add properties such as
lastTimeCalled
in order to save a timestamp on when it was called and only perform the call when the current time is bigger than that cache timestamp. - Implementing a Memento Pattern function: Instead of having a cache Object, have a cache Array (Stack). Push and pop the results of your calls in order to keep states of the results; through an interface, you can provide a "Undo" or "Restore" feature from the Client Side without having to hit a server.
-
Message Queueing: If your function is being called through
timeouts
, you can store analytics from it, or even recursive calls through a Cache Queue. You could even have a Web Worker check your Queue every once and then and perform actions according to what it finds in the queue.
Obviously this is no replacement for more powerful features such as the LocalStorage feature from HTML5, since it's store in the Javascript Memory Runtime. This means that it will disappear as soon as anyone refreshes the browser. However, this has the benefits of encapsulating the caching as part of the function, whereas other solutions require the creating of external variables, thus increasing dependency.
[1] From John Resig's page
Written by Jose Jesus Perez Aguinaga
Related protips
5 Responses
I know you borrowed the example but I just cringed at the loop. Easiest prime factorization speed up is to only check up to the square root of the number.
var check = Math.sqrt(num);
for ( var i = 2; i < check; i++ ) {
Hi Jose, here's a slightly changed version of your approach: https://coderwall.com/p/ueg_aw. I have used your example, please let me know what you think.
Maciej
Your example doesn't work because you are testing for a property of the cache object before the cache object has been defined. Also, you shouldn't check for a value of null, since null is a value that must be explicitly assigned beforehand. You could check for undefined or, better, just do an implicit comparison, since both null and undefined evaluate to true. If you want to do it your way, the beginning of your function should look like this:
function isPrime(num) { if (!isPrime.cache) { var isPrime.cache = {}; } else if (isPrime.cache[num]) { return isPrime.cache[num]; } ... } </code> </pre> BETTER YET, why not omit the whole cache property, and just make it a property of the function object itself? function isPrime(num) { if (isPrime[num]) { return isPrime[num]; } ... } </code> </pre>
@maciejsmolinski I like it! Have you seen Addy Osmani's article on Memoization? Might be even a way to go deeper!
- You are correct, silly me, should had done a
!isPrime.cache
first. - You are correct again! I learned afterwards the
!!
trick and have been using it afterwards. Thanks for the pointer. - Not a bad idea, although that may end up making your function to look up through the Object's Prototype Chain. That might have a some drawbacks in time consuming functions or complex objects. Have you checked maciejsmolinksi alternative?