All of the major browsers utilize some form of cache management to minimize redundant server calls. Since there is a certain amount of disk space allocated towards downloaded Web content, browsers can save time and network bandwidth by checking for the requested resource on the local hard drive before fetching it from the server. It is also a necessary component to the functioning of the back and forward buttons. One of the drawbacks of the growing proportion of dynamic content on the Web has been the corrosion of the gains in performance achieved by browser caching. The situation has only been made worse by the meteoric rise of Ajax, which has put the responsibility of calling server-side components squarely on the shoulders of Web developers. Therefore, we should consider the issue of whether or not to cache some of our data, just like other page content. In this two-part series, we'll build a client-side cache management system using the Prototype JavaScript Framework. Prototype will be of benefit for everything from facilitating Ajax calls to the handling of data collections to the displaying of HTML-formatted results on the page. In this installment we'll begin by building a couple of fairly basic caches, and explore some techniques for maintaining efficiency as complexity increases. In part two, we'll look at some different queuing algorithms to help manage the size and freshness of cached content.
Pros and Cons of Data Caching
Not every page that makes Ajax calls requires a caching mechanism. For example, a page that updates stock ticker information would likely gain very little by caching previous prices, as they may not repeat at all. In contrast, the search page of a Web application could benefit immensely from caching because clients may work with a small collection of data sets all day long. Just as browser scripts are often tailored to the site's functions, so too must caching suit the unique circumstances. The material that we'll be covering here will allow you to customize your cache in a way that most closely matches your site's needs. For the purpose of illustration, we'll set up a caching mechanism for an employee search page that displays the results within the same page, using Prototype's Ajax.Updater
object.
The Test Page
To see our cache at work, we'll need to design a search form and an HTML table to display the results. Keeping things simple, our form will contain four fields: "Last Name", "Given Names", "Age", and "Salary":
The AjaxCache
Class
Our first cache will store the data in a Prototype Hash
. A hash works like an associative array, but contains many methods to facilitate working with its items.
The window.onload
Event
The best time to initialize the cache is when the page has finished loading. Prototype has a mechanism for adding event handlers called Event.observe( Object objectToListenTo, string eventName, function handlerFunction )
. We'll use an anonymous function because we won't need to call it ourselves later. Instead, a listener will be notified when the event fires and execute our function:
The above code will create our cache in such a way that there will only be one in operation on the page at any time. Such a class is called a singleton because it can only be instantiated once. We can create a singleton in JavaScript by using the new
keyword in front of the function()
. You can test it yourself by adding var cacheInstance = new AjaxCache();
after the AjaxCache function's closing parentheses (}("employees1.htm");
). You'll get an error that the class cannot be created (the actual message will vary by browser). Your page may be complicated enough to warrant more than one cache, so you can define the AjaxCache
class without the new
keyword and then instantiate each cache object using the var cacheInstance = new AjaxCache();
syntax. Or, better still, you can use Prototype's Class.create()
method to instantiate the class. It was not an option here because the create()
method does not support singletons at this time. Below the AjaxCache
creation, we'll add a second call to Event.observe()
on the search button's click
event to call the searchForEmployee()
function. The Prototype Function.bind()
method is used to set the searchForEmployee()
function's context (the this
pointer) to our cache and to pass the form ID.