Last active
September 30, 2015 04:52
-
-
Save volpav/fa48d57d5ff1c287a488 to your computer and use it in GitHub Desktop.
Caching HTTP requests with Angular
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
angular.module('MyApp').factory('UrlCacheInterceptor', | |
function () { | |
'use strict'; | |
/** | |
* Returns value indicating whether required | |
* features are supported by the browser. | |
*/ | |
var featuresSupported = function () { | |
return ['URL', 'Blob', 'WeakMap'].every(function (feature) { | |
return typeof (window[feature]) !== 'undefined'; | |
}); | |
}; | |
return featuresSupported() ? (function () { | |
var cache = { | |
/* Stores cache rules. */ | |
rules: { | |
/** | |
* Returns cache rule that corresponds to the given HTTP request. | |
* @param config {object} HTTP request configuration. | |
*/ | |
get: function (config) { | |
return cache.rules.contents.filter(function (rule) { | |
return config.url === rule.url && | |
config.method.toLowerCase() === | |
(rule.method || 'get').toLowerCase(); | |
}).pop(); | |
}, | |
/* Stores cache rule contents. */ | |
contents: [ | |
{ url: '/profile' }, | |
{ url: '/logout', method: 'post', onInsert: function () { | |
/* On logout, clearing the entire cache. */ | |
cache.clear(); | |
/* No need to cache this HTTP response. */ | |
return true; | |
} } | |
] | |
}, | |
/* Stores cache contents. */ | |
contents: new WeakMap(), | |
/** | |
* Returns cache data that corresponds to the given rule. | |
* @param rule {object} Cache rule. | |
*/ | |
get: function (rule) { | |
return rule ? cache.contents.get(rule) : null; | |
}, | |
/** | |
* Inserts cache data that corresponds to the given rule. | |
* @param rule {object} Cache rule. | |
* @param data {object} Cache data. | |
*/ | |
set: function (rule, data) { | |
/* Binding to the document via in-memory URL. */ | |
var url = URL.createObjectURL( | |
new Blob([ | |
JSON.stringify(data) | |
], { type: 'application/json' } | |
) | |
); | |
cache.contents.set(rule, url); | |
return url; | |
}, | |
/** | |
* Removes cache data that corresponds to the given rule. | |
* @param rule {object} Cache rule. | |
*/ | |
remove: function (rule) { | |
var url = cache.contents.get(rule); | |
if (url) { | |
URL.revokeObjectURL(url); | |
cache.contents.delete(rule); | |
} | |
return url; | |
}, | |
/** | |
* Clears all the data from the cache. | |
*/ | |
clear: function () { | |
cache.rules.contents.forEach(cache.remove); | |
} | |
}; | |
return { | |
/** | |
* Intercepts the given HTTP request. | |
* @param config {object} HTTP request configuration. | |
*/ | |
request: function (config) { | |
/* Getting the in-memory URL that corresponds to cached data. */ | |
var url = cache.get(cache.rules.get(config)); | |
if (url) { | |
/* Overriding request URL - no need to go to the server. */ | |
config.url = url; | |
} | |
return config; | |
}, | |
/** | |
* Intercepts the given HTTP response. | |
* @param response {object} HTTP response. | |
*/ | |
response: function (response) { | |
/* Getting cache rule for the given HTTP request. */ | |
var rule = cache.rules.get(response.config); | |
/* Not caching same request multiple times. */ | |
if (rule && !cache.get(rule)) { | |
/* Allowing custom logic to execute before insertion. */ | |
if (!rule.onInsert || !rule.onInsert(response)) { | |
/* Caching response data. */ | |
cache.set(rule, response.data); | |
} | |
} | |
} | |
}; | |
})() : {}; | |
} | |
); |
Hey @szymko, just noticed your comment ;-) In this particular case, rules will not be GC'ed since they're being held by cache.contents
which is captured within the closure (which is part of the singleton).
I agree that if you want your rules to be dynamically populated, WeakMap
should probably be substituted with something else. I just wanted to get some sort of hash-table without writing any code for hashing on the key which is object
(you could, of course, just use {}
but then you better provide a contract for a hash function which felt like over-complicating things and driving away from the main point).
Cheers!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hey, that's cool, I just have one question though. Why is
WeakMap
used here? Is there any chance that one ofrules
will be garbage collected?