Recently, I posted a tutorial for creating a Grid bound to a Knockout ViewModel that loaded remote data from an OData feed. Today we are going to enhance this sample's performance by caching the data in Local Storage.
Too Many HTTP Requests
If we look at the HTTP Requests when paging or sorting, we can see the Grid calls the OData service for each action. Run the original sample and in the Chrome Developer Tools (or your browser tool of choice) watch the HTTP requests as you click page buttons in the Grid. For this scenario:
- We are on Page 1 which already called an HTTP request
- Click Page 2 and see second HTTP request
- Click back to Page 1 and notice a new HTTP request (this is what we want to avoid)
Notice the 3 HTTP requests in the screenshot. Ideally, we only want two, one for each page in the Grid that was requested.
Cache HTTP Requests for Better Performance
What we want to do is to cache each unique HTTP request so that it is only called once per session.
AmplifyJS to the Rescue
I am a big fan of AmplifyJS, especially the amplify.store
API. amplify.store
is meant to allow you to utilize all the latest storage technologies for those browsers that have them, while gracefully degrading for those without support. amplify.store
has a really simple API. It uses keys to get/set values. You can store whatever you want in the value (such as strings, numbers, objects, arrays). In order to use amplify, we need to add the following scripts to our existing app.
<!-- Local storage-->
<script src="http://cdn.wijmo.com/external/amplify.core.min.js" type="text/javascript"></script>
<script src="http://cdn.wijmo.com/external/amplify.store.min.js" type="text/javascript"></script>
Next we will add a utility function to our ViewModel that builds a unique key for each HTTP request. It uses the params passed to the OData service to make the unique key.
getUniqueStorageKey: function () {
return "size" + viewModel.pageSize() + "sort" + viewModel.sortCommand() + "page" + viewModel.pageIndex();
}
Now, we need to store the unique data for the current ajax request. All we have to do is add two lines of code in our ajax success
callback. We are storing two things here.
- Rows of data for this unique request (object, with array inside)
- Total row count (number)
Note: We are using sessionStorage()
because I don't want the cache to persist when the window is closed. This gives the end user (and myself) an easy way of clearing the cache. For persistent Local Storage you can use storage()
instead. But make sure you have a means of clearing it out so you don't get stuck with stale data on your clients machine.
amplify.store.sessionStorage(viewModel.getUniqueStorageKey(), { d: data });
amplify.store.sessionStorage("total", result.d.__count);
Lastly, inside our load
function we want to add a check for cached data and use it instead of making an ajax request.
if (amplify.store.sessionStorage(viewModel.getUniqueStorageKey()) != null) {
var arr = [];
var data = amplify.store.sessionStorage(viewModel.getUniqueStorageKey()).d;
var total = amplify.store.sessionStorage("total");
$.each(data, function (i) {
arr.push(new product(data[i]));
});
viewModel.totalRows(total);
viewModel.dataRows(arr);
}
Run it!
That's it, just run the app and watch your HTTP requests in Chrome. Follow the same steps as before and observe that there are no longer any duplicate HTTP requests when that data has already been loaded! Notice, only two HTTP requests where there used to be 3. Now all HTTP requests are cached and will only be made once per session.
Summary
When building ajax applications, it is easy to make redundant HTTP requests. Try to cache as much as you can in order to increase your app's performance. AmplifyJS has a great storage API for easily using Local Storage across different browsers.