- registers are faster than memory,
- memory is faster than external storage,
- external storage is faster than the local network, and
- the local network is faster than the Internet
provide performance hints for web applications. One such hint is that we should store components on the local hard disk rather than retrieving them from a server.
Today we’re going to have some fun! We’re going to take that hint and combine it with memorization, a concept that’s been around since the 1990’s or earlier.
Memoization tells us to go get what we need the first time we need it, but to cache it locally so we don’t have to go get it a second time. The following JavaScript function serves that purpose.
This function is written to demonstate a point, not for inclusion in production code. It still needs rigorous error checking and better error handling.
//This code sample shows how to download JavaScript //snippets on demand, but only if they weren't //previously downloaded. When the snippet is //downloaded the first time, it is placed into local //storage. Whenever it is requested thereafter, it is //pulled from local storage instead of being downloaded //a second time. //This code was released into the public domain by its //author, Warren Gaebel, on 2012.07.02. 001 function getCode (snippetName) { 002 snippetVariableName = "jss$" + snippetName; 003 if (localStorage && localStorage[snippetVariableName]) { 004 return localStorage[snippetVariableName]; 005 }else{ 006 var httpRequest = new XMLHttpRequest(); 007 httpRequest.open("GET", snippetName+".js", false); 008 httpRequest.send(); 009 if (httpRequest.status == 200 || httpRequest.status == 304) { 010 if (localStorage && !localStorage[snippetVariableName]) { 011 localStorage[snippetVariableName] = httpRequest.responseText; 012 }//end if 013 return httpRequest.responseText; 014 }else{ //httpRequest.status != 200 && httpRequest.status != 304 015 alert("Error executing XMLHttpRequest call!"); 016 }//end if (httpRequest.status == 200 || httpRequest.status == 304) 017 }//end if (localStorage && localStorage["fn$"+ snippetName]) 018 }//end function activateFunction (snippetName) { 019 </script></head><body> 020 <script type="text/JavaScript"> 021 eval(getCode("alertMeSnippet")); </script></body></html>
Line 3 determines whether localStorage is available and if the code snippet is in it. If so, it returns the snippet from localStorage in line 4. If it is not available in localStorage, lines 6-16 get it from the server, save it in localStorage, and return it to the caller.
Lines 6-8 get the snippet from the server. Line 9 checks to see if there were any problems. Codes 200 and 304 tell us that everything is fine. [200 says we got it from the server; 304 says we got it from a cache.]
If everything went well up to this point, lines 10-12 add the snippet to localStorage if localStorage exists and the snippet is not already there. Line 13 then returns the snippet to the caller.
If something goes wrong, line 15 uses an alert box to let us know.
Line 21 is an example invocation. It calls getCode to retrieve alertMeSnippet.js from localStorage or the server. The eval function then executes the snippet. Note that it could just as easily have stored the snippet in a string variable for other purposes.
This technique will most likely be used to download function definitions. If each function (or family of functions) is in its own .js file, use getCode to retrieve it. The functions will be available for use after the code is eval’d (but watch out for potential scope issues).
Room for Improvement
Some issues must be addressed before using this technique:
- Not all browsers support localStorage.
- What do we do if we don’t get a 200 or 304 return code?
- There’s a limited amount of space in localStorage. Currently, if we want more, we’re out of luck.
- When we move to the next version of the snippet, we can simply change its name. However, the old snippet is still in localStorage. We need to remember to delete the old one or we’ll soon run out of space.
- The above code assumes that the snippet is in the same server directory as the current web page.
- Each invocation of the above getCode is synchronous. Asynchronous would be better.
- If the web page’s name or location changes, it gets a brand new, empty localStorage. The old localStorage is still there, cluttering up the user’s hard disk. More importantly, whatever was saved in it is now inaccessible to the web page.