Created
November 14, 2014 12:23
-
-
Save seeruk/6156c03df369239444c6 to your computer and use it in GitHub Desktop.
Angular Caching Service
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("td.service.cache", [ "td.service.timestamp" ]) | |
.factory("CacheService", function($q, TimestampService) { | |
"use strict"; | |
var CacheService = {}, | |
pendingActions = {}, | |
prefix = "CacheService_"; | |
/** | |
* Generate a cache key | |
* | |
* @param {String} key | |
* | |
* @return {String} | |
*/ | |
function generateKey(key) { | |
return prefix + key; | |
} | |
/** | |
* Get an item from the cache | |
* | |
* @param {String} key | |
* | |
* @return {Mixed} | |
*/ | |
CacheService.get = function(key) { | |
var deferred = $q.defer(); | |
if (!pendingActions[key]) { | |
pendingActions[key] = $q.defer(); | |
pendingActions[key].resolve(); | |
} | |
pendingActions[key].promise.finally(function() { | |
var item = JSON.parse(localStorage.getItem(generateKey(key))); | |
if ( | |
item !== null && | |
typeof item === "object" && | |
item.value && | |
item.expires % 1 === 0 && | |
(item.expires === 0 || item.expires > TimestampService.unix()) | |
) { | |
deferred.resolve(item.value); | |
} else { | |
deferred.reject(); | |
} | |
}); | |
pendingActions[key] = deferred; | |
return deferred.promise; | |
}; | |
/** | |
* Set an item in the cache | |
* | |
* @param {String} key | |
* @param {Mixed} value | |
* @param {Integer} ttl | |
* | |
* @return {Mixed} | |
*/ | |
CacheService.set = function(key, value, ttl) { | |
if (typeof value === "function") { | |
return CacheService.set(key, value(), ttl); | |
} | |
var deferred = $q.defer(); | |
if (!pendingActions[key]) { | |
pendingActions[key] = $q.defer(); | |
pendingActions[key].resolve(); | |
} | |
pendingActions[key].promise.finally(function() { | |
// Transform value into promise | |
$q.when(value) | |
.then( | |
function(value) { | |
var expires = (ttl !== undefined) ? | |
TimestampService.unix() + ttl : | |
0; | |
try { | |
localStorage.setItem(generateKey(key), JSON.stringify({ | |
value: value, | |
expires: expires | |
})); | |
deferred.resolve(value); | |
return deferred; | |
} catch (e) { | |
deferred.reject(e); | |
if ( | |
e.name === "QuotaExceededError" || | |
e.name === "NS_ERROR_DOM_QUOTA_REACHED" | |
) { | |
console.warn( | |
"[CacheService]", | |
"localStorage storage limit reached. Flushing." | |
); | |
CacheService.empty(); | |
} else { | |
throw e; | |
} | |
} | |
}, | |
function() { | |
deferred.reject(); | |
} | |
) | |
; | |
}); | |
pendingActions[key] = deferred; | |
return deferred.promise; | |
}; | |
/** | |
* Wrap get/set so that functionality can be easily cached | |
* | |
* @param {String} key | |
* @param {Function} callback | |
* @param {Integer} ttl | |
* | |
* @return {Mixed} | |
*/ | |
CacheService.proxy = function(key, value, ttl) { | |
if (value === undefined) { | |
// @todo: Make exceptional | |
throw new Error(); | |
} | |
return CacheService.get(key) | |
.catch(function() { | |
return CacheService.set(key, value, ttl); | |
}) | |
; | |
}; | |
/** | |
* Remove an item from the cache | |
* | |
* @param {String} key | |
*/ | |
CacheService.delete = function(key) { | |
if (pendingActions[key]) { | |
pendingActions[key].reject(); | |
delete pendingActions[key]; | |
} | |
localStorage.removeItem(generateKey(key)); | |
return $q.when(); | |
}; | |
/** | |
* Flush the entire cache | |
*/ | |
CacheService.empty = function() { | |
var promises = []; | |
Object | |
.keys(localStorage) | |
.filter(function(key) { | |
return key.substr(0, prefix.length) === prefix; | |
}) | |
.map(function(key) { | |
return key.substr(prefix.length); | |
}) | |
.concat(Object.keys(pendingActions)) | |
.forEach(function(key) { | |
promises.push(CacheService.delete(key)); | |
}) | |
; | |
return $q.all(promises); | |
}; | |
return CacheService; | |
}) | |
; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment