Using Promises to Cache Static AJAX JSON Data

This article showcases a useful caching strategy for static data that is fetch via AJAX. We will use jQuery to setup a promise and cache the data in the localStorage for subsequent page loads or data loads.

Getting ready

A modern web browser supporting localStorage and JSON. Also, a basic understanding of promises[2] is helpful.

How do it…

Here is the code:

(function($) {
  var oKeyDeferredMap = {};
  
  function fnReadData(sKey) {
    var sValue = window.localStorage.getItem(sKey);
    return sValue ? JSON.parse(sValue) : sValue;
  }
  
  function fnWriteData(sKey, oData) {
    var sValue = JSON.stringify(oData);
    window.localStorage.setItem(sKey, sValue);
  }
  
  $.cachedAjaxPromise = function(sUrl, oAjaxOptions) {
    var oDeferred = oKeyDeferredMap[sUrl];
    var sValue;
    
    if (!oDeferred) {
      oDeferred = new jQuery.Deferred();
      oKeyDeferredMap[sUrl] = oDeferred;
      sValue = fnReadData(sUrl);
      
      if (sValue) {
        oDeferred.resolve(sValue);
      }
      
      if (!oAjaxOptions) {
        oAjaxOptions = {};
      }
      
      $.extend(oAjaxOptions, {
        error: function(oXHR, sTextStatus, sErrorThrown) {
          console.log('customer info request failed: ' + sErrorThrown);
          oDeferred.resolve(null);
        },
        success: function(oData) {
          // making assumption that data is JSON
          fnWriteData(sUrl, oData);
          oDeferred.resolve(oData);
        }
      });
      
      $.ajax(sUrl, oAjaxOptions);
    }
    
    return oDeferred.promise();
  };
}(jQuery));

Here is how to use the code:

oPromise = $.cachedAjaxPromise('/someURL').done(function(o) {
  console.dir(o);
});

Here is a codepen demoing the code:

See the Pen HtJcD by Matt Snider (@mattsnider) on CodePen.


How it works…

The code snippet adds a new function to jQuery cachedAjaxPromise which should be passed a URL returning a static JSON and returns a promise that will be resolved with the JSON object. The function checks the local storage for a value stored at the key of the url. If a value exists, then resolve the promise. If the value doesn’t exist, then fetch it from the server, cached into localStoage, and the promise is resolved or rejected based on the AJAX response cycle. All cached values are marshalled and unmarshalled using the JSON infrastructure to and from strings. Lastly, the jQuery deferred object, is also cached, to prevent duplicate AJAX requests or calls to the localStorage when promises for the same url are created.

To use cachedAjaxPromise, provide a url and chain a done or then function. The success callback provided will be pass the JSON data. If you look at the example pen, you will see that the first time the pen is loaded (or after you clear the localStorage) it takes about two seconds (simulated AJAX request), but subsequent page loads resolve the data in millisecond (from localStorage). You may pass a second argument, as the configuration options to pass into $.ajax, but the success and error functions will be overwritten for use with the promise system.

This a very simple example that is dependent on modern web browser technology. If you need to support legacy browsers, then you may need a polyfill the JSON[3] library and localStorage[4].

References

  1. jQuery Deferred
  2. Promises in Wicked Detail
  3. jQuery JSON Polyfill
  4. PersistJs - a localStorage Polyfill

Connecting to Github and EC2 Through a Proxy on Port 80 or 443

Today we’ll cover how to connect to github and EC2 through a draconian proxy allowing only port 80 and 443. Github uses SSH, so like EC2 it can be connected to using SSH tunnelling. This article is based on a blog post by tachang[1], which needed some additional explanation and changes to work behind my proxy. I will be explaining how to connect on a unix-based machine, but these settings should also work on ...

Introducing Gaming Engine - Snake Demo v1

In my not so copious spare time over the past few months, I’ve been working on a game engine to power two dimensional board-based games. The engine has a long way to go, but I have reach the first demo milestone and wanted to share it with you. Here is a basic version the snake game written using the game engine. It illustrates a working main thread, responsiveness to keyboard commands, interaction between a ...

jQuery Function for Change Event and Delayed Keydown Event

In my experience, it is rare to assign only a change event to a text input, as any callback that would be executed for the change event should also be called on a key event as well, but with a slight delay (think how an autocomplete shows results as you type). This is a common pattern and I was surprised to not immediately find a jQuery plugin implementing it, so I decided to add one ...

Merge Sort

Continuing to evaluate efficient sorting algorithms, today we’ll look at merge sort. Merge sort[1] is a comparison sort using a divide and conquer algorithm, developed by John von Neumann[2] in 1945. It recursively divides the list into smaller sublists of length one, then repeatedly merges the sublists in order until there is only one sublist left. It has a worst case runtime of (O(nlogn)), making it worst-case more efficient than Quicksort.

Quicksort

We’ve looked a variety of in-efficient sorting algorithms, today we’ll look at Quicksort (aka. partition exchange sort), as a first foray into faster and more frequently used sorting algorithms. Quicksort[1] is a comparison sort using a divide and conquer algorithm, developed by Tony Hoare[2] in 1960. It recursively divides the list into smaller lists around a pivot value and sorts them, which means much smaller data sets when actually sorting. It has a ...

Applying Grayscale Using CSS Filters

This technique has been around for a while, but it’s powerful and worth sharing. Using the filter CSS property you can apply visual effects to your elements, including the grayscale we’ll be discussing here. For my CV I wanted my image muted most of the time, but pop when it becomes the focus of the viewer (ie. they mouse over it), so I used a filter to apply grayscale by default and remove grayscale ...

CSS Interview Questions

Lately, I have been interviewing many engineers who are interested in a CSS contractor position, and am thoroughly disheartened by the number of candidates who put CSS expert on their resume, but don’ even know the basics of CSS. This article will discuss the ten questions I usually ask, including the answer and why I ask the question. My hope is not to just give the answer, but to educate as well.

Questions

Each question ...

Hoisting 102 - Examining a Global Context Hoisting Gotcha

In an earlier article we covered Variable Hoisting in JavaScript. At the time, I did not expect to face a hoisting related code bug so quickly. And given the circumstances of the bug, I expect it to be one of the most common hoisting problems, so I wanted to share it.

How do it…

Let’s jump right into the setup. You have a function that is defined in one JavaScript file (this file ...

jQuery Widget for Dynamic Input Helper Text

This is a proof of concept widget that I demoed for work. The desire is to update some text according to a regex and replacement, when an input field changes. This will allow developers to show a message and/or format the input value, so users understand they do not need to enter meta characters and see the result of their input (think phone or identification numbers). I built a simple jQuery plugin that can be ...