Last Updated: December 30, 2020
·
57.35K
· jdobry

Power up Angular's $http service with caching

Power up Angular's $http service with caching

<a name="intro"></a>

Intro

Angular.js rocks and you're flying high making AJAX requests left and right. Inject $http, shoot off a request and boom—the promise-based service hits your error or success callback.

$http.get('/foo/bar/' + itemId)
  .success(function (data) {
    data; // { foo: 'bar' }
  })
  .error(function (data, status, headers, config) {
    // uh oh
  });

Okay, so you've got your data from your backend—now what? This particular bit of data doesn't change very often, and you're wondering why your client repeatedly makes the same request for the same data—what a waste! Save your users some time and bandwidth and tell that request to use caching!

$http.get('/foo/bar/' + itemId, { cache: true })
  .success(function (data) {
    data; // { foo: 'bar' }
  })
  .error(function (data, status, headers, config) {
    // uh oh
  });

Sweet! Now, the first time $http sends a GET request to /foo/bar/123, for example, the response will be stored in a cache named "$http". This cache is created by Angular's $cacheFactory as the default cache for the $http service when Angular boots up. { cache: true } tells the $http service to cache the response to that particular request in $http's default cache. You don't have to do anything else. Any subsequent requests to /foo/bar/123 will simply use the cached reponse. Neat!

If you want you can actually reference $http's default cache and retrieve items, remove items, clear the cache, etc. Just inject $cacheFactory into whatever you're working with, and reference $http's default cache via:

var $httpDefaultCache = $cacheFactory.get('$http');

You can retrieve the cached response to the above GET requested by using the absolute url of the request.

var cachedData = $httpDefaultCache.get('http://myserver.com/foo/bar/123'); // { foo: 'bar' }

You can clear that item from the cache:

$httpDefaultCache.remove('http://myserver.com/foo/bar/123');

Or clear the whole cache:

$httpDefaultCache.removeAll();

See $cacheFactory for a complete reference.

<a name="lru-cache"></a>

LRU Cache

What if you don't want $http's default cache to store every response? Simple: turn it into an LRU Cache (Least Recently Used).

// Create a new cache with a capacity of 10
var lruCache = $cacheFactory('lruCache', { capacity: 10 });

// Use the new cache for this request
$http.get('/foo/bar/' + itemId, { cache: lruCache })
  .success(function (data) {
    data; // { foo: 'bar' }
  })
  .error(function (data, status, headers, config) {
    // uh oh
  });

The reponse to each request to /foo/bar/:itemId will be cached, but this time the cache only has a capacity of 10. When the eleventh unique request is made the least recently accessed item will be removed from the caching, keeping the total number of cached items at 10. This cache maintains a list of its items in order of how recently they have been accessed so it knows which one to remove when storing a new item would exceed the capacity.

<a name="setting-default"></a>

Setting a default cache

As shown in the LRU example you can tell an $http request to use a cache other than $http's default cache, but what if you wants to change $http's default cache? It's easy:

$httpProvider.defaults.cache = $cacheFactory('myNewDefaultCache', { capacity: 100 });

$http will now use myNewDefaultCache as its default cache.

<a name="advanced-caching"></a>

Advanced Caching

What if you want to cache data to improve user experience, but the data is liable to change once a day, or every few hours, etc. You would want to make sure your cached data gets cleared at least once a day, every ten minutes, etc. Unfortunately Angular's $cacheFactory does not provide any such capabilities.

You could hack something together using setInterval() or setTimeout(), but you don't want to do that. To solve this problem I created angular-cache, a drop-in Angular module that gives you access to $angularCacheFactory, which creates caches with more capabilities. With angular-cache your caches can periodically clear themselves with cacheFlushInterval. When adding to a cache you can specify a maxAge, which is the maximum amount of time that particular item will be in the cache before it is automatically removed. Or you can specifiy a maxAge for the whole cache which will be applied to every item added to the cache.

I'll refer you to the angular-cache documentation for further reference.

Happy caching!

10 Responses
Add your response

I'd just like to emphasize that angular's built-in cacheFactory's simplicity is its greatest strength, not a weakness. It has a tiny footprint that keeps angular's code small. It's great! All it is is an object and a linked list. And angularCacheFactory is great too! It's there when you need more, and uses a more complex, but more flexible heap structure.

Good post. Thanks for your work.

over 1 year ago ·

Thanks!

p.s. angular-cache 2.0.0 is out tonight with a new website! http://jmdobry.github.io/angular-cache

over 1 year ago ·

Excellent post. Question for you.

How can you tell if your request is sucessfully coming from a cached source? I watch my network tab on dev-tools and don't see it at all. Typically it will tell me if it's coming from a cached source.

When angular is handling to cache and making the response for the $http request does it not show up on the network tab of dev-tools?

I also am not seeing much improvement from enabling cache, which I thought I would as the the JSON data I am attempting to cache is roughly 550kb. I was thinking if we cached this large data-set then going between views that it would speed up, but I don't seem to be getting any benefits from cache here. Any ideas?

Basically I have a large json file that serves as a datastore for different views. Some views require only a few of the json objects, one view (an archive) requires almost all the json objects. When switching to that view it seems sluggish, even though I have injected that service on the main page of the ngApp. I was thinking since the data is cached and has already been called from a service that switching to this view would be quick, but it seems to be blocked somewhere.

over 1 year ago ·

There will be no request listed on the dev-tools network tab when a resource is retrieved Angular's cache.

As far as seeing improvement, there are other factors involved than just enabling the cache: network latency on your local machine vs latency between a user and your servers thousands of miles away, browser performance, etc. However, with 550kb, I would definitely expect to see a performance improvement. Something along the lines of hundreds of milliseconds to retrieve the resource from the server and 0-10ms to retrieve it from the cache.

It is abnormal to not see any performance improvement. I see performance jumps even when my resource is 20kb. If the cache is enabled, the performance gain should be obvious (orders of magnitude better).

over 1 year ago ·

Great post and cool module!

Thank you @jdobry!

over 1 year ago ·

Caching the following http://myserver.com/foo/bar/123, what happens to 123 if my next request is http://myserver.com/foo/bar/124? is the previous 123 cached request is being cleared? Thank You!

over 1 year ago ·

olanger0919, to answer your question:

"Caching the following http://myserver.com/foo/bar/123, what happens to 123 if my next request is http://myserver.com/foo/bar/124? is the previous 123 cached request is being cleared?"

No, the previous 123 cached request is not cleared. It is stored until you close or refresh the page.

If you then check 125, 126, 127 etc and then go back to 123 later, it will load 123 from the cache. (Unless you've closed or reloaded the page, in which case the cache is cleared).

Angular's built in cache functionality in $http works differently than server-side caching in that Angular's cache is cleared every time the page is refreshed. There are other options that let you use, for instance, localStorage and that can persist after a browsing "session" (i.e. after closing or refreshing the page). But the default "cache" option in $http does not retain any cached data beyond the lifecycle of a given pageload.

over 1 year ago ·

Thanks for this post! This helped solve the exact caching dilemma I've had for a while now :)

over 1 year ago ·

How long is the cache stored for by default? Lets say I have an application that retrieves some data from Azure Mobile Service, but I only need to retrieve it once, is the data cleared from the cache when the user closes the application or is it still there when he starts the app up again?

over 1 year ago ·

Refresh the page, cache is cleared.

over 1 year ago ·