Lifespan of LocalStorage data [duplicate] - html

For how long is data stored in localStorage (as part of DOM Storage in HTML5) available? Can I set an expiration time for the data which I put into local storage?

I would suggest to store timestamp in the object you store in the localStorage
var object = {value: "value", timestamp: new Date().getTime()}
localStorage.setItem("key", JSON.stringify(object));
You can parse the object, get the timestamp and compare with the current Date, and if necessary, update the value of the object.
var object = JSON.parse(localStorage.getItem("key")),
dateString = object.timestamp,
now = new Date().getTime().toString();
compareTime(dateString, now); //to implement
Alternatively, you could use a light-weight wrapper like localstorage-slim.js which handles this for you.

It's not possible to specify expiration. It's completely up to the user.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
Of course, it's possible that something your application stores on the client may not be there later. The user can explicitly get rid of local storage, or the browser may run into space considerations. It's good to program defensively. Generally however things remain "forever" based on some practical definition of that word.
edit — obviously, your own application can actively remove stuff if it decides it's too old. That is, you can explicitly include some sort of timestamp in what you've got saved, and then use that later to decide whether or not information should be flushed.

You can use lscache. It handles this for you automatically, including instances where the storage size exceeds the limit. If that happens, it begins pruning items that are the closest to their specified expiration.
From the readme:
lscache.set
Stores the value in localStorage. Expires after specified number of minutes.
Arguments
key (string)
value (Object|string)
time (number: optional)
This is the only real difference between the regular storage methods. Get, remove, etc work the same.
If you don't need that much functionality, you can simply store a time stamp with the value (via JSON) and check it for expiry.
Noteworthy, there's a good reason why local storage is left up to the user. But, things like lscache do come in handy when you need to store extremely temporary data.

Brynner Ferreira, has brought a good point: storing a sibling key where expiration info resides. This way, if you have a large amount of keys, or if your values are large Json objects, you don't need to parse them to access the timestamp.
here follows an improved version:
/* removeStorage: removes a key from localStorage and its sibling expiracy key
params:
key <string> : localStorage key to remove
returns:
<boolean> : telling if operation succeeded
*/
function removeStorage(name) {
try {
localStorage.removeItem(name);
localStorage.removeItem(name + '_expiresIn');
} catch(e) {
console.log('removeStorage: Error removing key ['+ key + '] from localStorage: ' + JSON.stringify(e) );
return false;
}
return true;
}
/* getStorage: retrieves a key from localStorage previously set with setStorage().
params:
key <string> : localStorage key
returns:
<string> : value of localStorage key
null : in case of expired key or failure
*/
function getStorage(key) {
var now = Date.now(); //epoch time, lets deal only with integer
// set expiration for storage
var expiresIn = localStorage.getItem(key+'_expiresIn');
if (expiresIn===undefined || expiresIn===null) { expiresIn = 0; }
if (expiresIn < now) {// Expired
removeStorage(key);
return null;
} else {
try {
var value = localStorage.getItem(key);
return value;
} catch(e) {
console.log('getStorage: Error reading key ['+ key + '] from localStorage: ' + JSON.stringify(e) );
return null;
}
}
}
/* setStorage: writes a key into localStorage setting a expire time
params:
key <string> : localStorage key
value <string> : localStorage value
expires <number> : number of seconds from now to expire the key
returns:
<boolean> : telling if operation succeeded
*/
function setStorage(key, value, expires) {
if (expires===undefined || expires===null) {
expires = (24*60*60); // default: seconds for 1 day
} else {
expires = Math.abs(expires); //make sure it's positive
}
var now = Date.now(); //millisecs since epoch time, lets deal only with integer
var schedule = now + expires*1000;
try {
localStorage.setItem(key, value);
localStorage.setItem(key + '_expiresIn', schedule);
} catch(e) {
console.log('setStorage: Error setting key ['+ key + '] in localStorage: ' + JSON.stringify(e) );
return false;
}
return true;
}

Here highly recommended to use sessionStorage
it is same as localStorage but destroy when session destroyed / browser close
also localStorage can share between tabs while sessionStorage can use in current tab only, but value does not change on refresh page or change the page
sessionStorage is also useful to reduce network traffic against cookie
for set value use
sessionStorage.setItem("key","my value");
for get value use
var value = sessionStorage.getItem("key");
click here for view api
all ways for set are
sessionStorage.key = "my val";
sessionStorage["key"] = "my val";
sessionStorage.setItem("key","my value");
all ways for get are
var value = sessionStorage.key;
var value = sessionStorage["key"];
var value = sessionStorage.getItem("key");

While local storage does not supply an expiration mechanism, cookies do. Simply pairing a local storage key with a cookie provides an easy way to ensure that local storage can be updated with the same expiration parameters as a cookie.
Example in jQuery:
if (!$.cookie('your_key') || !localStorage.getItem('your_key')) {
//get your_data from server, then...
localStorage.setItem('your_key', 'your_data' );
$.cookie('your_key', 1);
} else {
var your_data = localStorage.getItem('your_key');
}
// do stuff with your_data
This example sets a cookie with the default parameter to expire when the browser is closed. Thus, when the browser is closed and re-opened, the local data store for your_data gets refreshed by a server-side call.
Note that this is not exactly the same as removing the local data store, it is instead updating the local data store whenever the cookie expires. However, if your main goal is to be able to store more than 4K client-side (the limitation for cookie size), this pairing of cookie and local storage will help you to accomplish a larger storage size using the same expiration parameters as a cookie.

The lifecycle is controlled by the application/user.
From the standard:
User agents should expire data from the local storage areas only for security reasons or when requested to do so by the user. User agents should always avoid deleting data while a script that could access that data is running.

From the W3C draft:
User agents should expire data from the local storage areas only for security reasons or when requested to do so by the user. User agents should always avoid deleting data while a script that could access that data is running.
You'll want to do your updates on your schedule using setItem(key, value); that will either add or update the given key with the new data.

// Functions
function removeHtmlStorage(name) {
localStorage.removeItem(name);
localStorage.removeItem(name+'_time');
}
function setHtmlStorage(name, value, expires) {
if (expires==undefined || expires=='null') { var expires = 3600; } // default: 1h
var date = new Date();
var schedule = Math.round((date.setSeconds(date.getSeconds()+expires))/1000);
localStorage.setItem(name, value);
localStorage.setItem(name+'_time', schedule);
}
function statusHtmlStorage(name) {
var date = new Date();
var current = Math.round(+date/1000);
// Get Schedule
var stored_time = localStorage.getItem(name+'_time');
if (stored_time==undefined || stored_time=='null') { var stored_time = 0; }
// Expired
if (stored_time < current) {
// Remove
removeHtmlStorage(name);
return 0;
} else {
return 1;
}
}
// Status
var cache_status = statusHtmlStorage('cache_name');
// Has Data
if (cache_status == 1) {
// Get Cache
var data = localStorage.getItem('cache_name');
alert(data);
// Expired or Empty Cache
} else {
// Get Data
var data = 'Pay in cash :)';
alert(data);
// Set Cache (30 seconds)
if (cache) { setHtmlStorage('cache_name', data, 30); }
}

If you’re familiar with the browsers locaStorage object, you know that there’s no provision for providing an expiry time. However, we can use Javascript to add a TTL (Time to live) to invalidate items in locaStorage after a certain period of time elapses.
function setLocalStorageItem(key, value, ttl) {
// `item` is an object which contains the original value
// as well as the time when it's supposed to expire
let item = {
value: value,
expiry: ttl ? Date.now() + ttl : null
};
localStorage.setItem(key, JSON.stringify(item));
}
function getLocalStorageItem(key) {
let item = localStorage.getItem(key);
// if the item doesn't exist, return null
if (!item) return null;
item = JSON.parse(item);
// compare the expiry time of the item with the current time
if (item.expiry && Date.now() > item.expiry) {
// If the item is expired, delete the item from storage and return null
localStorage.removeItem(key);
return null;
}
return item.value;
}

You can try this one.
var hours = 24; // Reset when storage is more than 24hours
var now = Date.now();
var setupTime = localStorage.getItem('setupTime');
if (setupTime == null) {
localStorage.setItem('setupTime', now)
} else if (now - setupTime > hours*60*60*1000) {
localStorage.clear()
localStorage.setItem('setupTime', now);
}

If anyone still looking for a quick solution and don't want dependencies like jquery etc I wrote a mini lib that add expiration to local / session / custom storage, you can find it with source here:
https://github.com/RonenNess/ExpiredStorage

If someone using jStorage Plugin of jQuery the it can be add expiry with setTTL function if jStorage plugin
$.jStorage.set('myLocalVar', "some value");
$.jStorage.setTTL("myLocalVar", 24*60*60*1000); // 24 Hr.

Javascript localStorage do not have any options to set expire time
Then i use these functions to check supports, Set and Get
function ls_support(){
return "localStorage" in window&&window["localStorage"]!==null;
}
function lsset(key,val,exp){
if(ls_support()){
if(!exp) exp=600;// = 10 minutes Default
localStorage[key]=
JSON.stringify({
"val":val,
"exp":~~((new Date()).getTime()/1000)+exp
});
}
}
function lsget(key){
if(ls_support()){
str=localStorage[key];
if("undefined"!=typeof(str)&&str.length){
try{// is json or not
json=JSON.parse(str);
}catch(e){// if variable not set via lsset func
//json.exp=false;// will return null
return str;// will return original variable
}
if(json.exp){// variable setted via lsset func
if(~~((new Date()).getTime()/1000)>json.exp){// expired
delete localStorage[key];
}else{
return json.val;
}
}
}
}
return null;
}
And it seems works fine :

Workaround using angular and localforage:
angular.module('app').service('cacheService', function() {
return {
set: function(key, value, expireTimeInSeconds) {
return localforage.setItem(key, {
data: value,
timestamp: new Date().getTime(),
expireTimeInMilliseconds: expireTimeInSeconds * 1000
})
},
get: function(key) {
return localforage.getItem(key).then(function(item) {
if(!item || new Date().getTime() > (item.timestamp + item.expireTimeInMilliseconds)) {
return null
} else {
return item.data
}
})
}
}
})

#sebarmeli's approach is the best in my opinion, but if you only want data to persist for the life of a session then sessionStorage is probably a better option:
This is a global object (sessionStorage) that maintains a storage area
that's available for the duration of the page session. A page session
lasts for as long as the browser is open and survives over page
reloads and restores. Opening a page in a new tab or window will cause
a new session to be initiated.
MDN: sessionStorage

For the benefit of searchers:
Like Fernando, I didn't want to add a load of json when the values stored were simple. I just needed to track some UI interaction and keep the data relevant (e.g. how a user used an ecommerce site before checking out).
This will not meet everyones criteria, but will hopefully be a quick copy+paste starter for someone and save adding another lib.
NOTE: This would not be good if you need to retrieve the items individually.
// Addition
if(window.localStorage){
localStorage.setItem('myapp-' + new Date().getTime(), 'my value');
}
// Removal of all expired items
if(window.localStorage){
// two mins - (1000 * 60 * 20) would be 20 mins
var expiryTime = new Date().getTime() - (1000 * 60 * 2);
var deleteRows = [];
for(var i=0; i < localStorage.length; i++){
var key = localStorage.key(i);
var partsArray = key.split('-');
// The last value will be a timestamp
var lastRow = partsArray[partsArray.length - 1];
if(lastRow && parseInt(lastRow) < expiryTime){
deleteRows.push(key);
}
}
// delete old data
for(var j=0; j < deleteRows.length; j++){
localStorage.removeItem(deleteRows[j]);
}
}

function setStorage(name,value){
return localStorage.setItem(name,JSON.stringify({value:value,timestamp:Math.round((new Date()).getTime()/1000)}));
}
function getStorage(name,timeout){
var object = JSON.parse(localStorage.getItem(name));
if(object){
if(Math.round((new Date()).getTime()/1000) < (object.timestamp+timeout)){
return object.value;
}else{
localStorage.removeItem(name);
}
}
return false;
}

Related

Lock Service not always locking in Google Apps Script

In my Google Apps Script application, there's a place where I generate a unique, incrementing order number. To achieve this, I make use of the builtin LockService and PropertiesService.
I store a number as a script property. When I need a new order number, the application fetches the current value, increments it, and saves the new value for next time. To make sure that two people running the application don't get the same number, access to the script property is placed inside of a script lock, or mutex.
This works well, and hundreds of calls go through the function each day. But twice in the past month, two users have ended up with the same order number.
// Increment the existing value, and return our new value.
function getPropIncrementViaMutex(propId) {
try {
var scriptProperties = PropertiesService.getScriptProperties();
var lock = LockService.getScriptLock();
var success = false;
var prevValue = null;
var newValue = null;
var wasSet = null;
while (!success) {
success = lock.tryLock(500);
if (!success) {
Utilities.sleep(1000);
} else {
prevValue = Number(scriptProperties.getProperty(propId));
scriptProperties.setProperty(propId, prevValue + 1);
newValue = Number(scriptProperties.getProperty(propId));
lock.releaseLock();
wasSet = (newValue === (prevValue + 1));
}
}
if (wasSet) {
return newValue;
} else {
throw new Error("Error incrementing order number. Previous Value: " + prevValue + " New Value: " + newValue);
}
} catch(e) {
if (lock) {
lock.releaseLock();
}
throw e;
}
}
Is there something I'm doing wrong here? Is the problem on Google's end?
A colleague suggested increasing the lock time in lock.tryLock(500) to something higher like lock.tryLock(800).
He also suggests that when releasing the lock, to call Utility.sleep(300) beforehand, so the script has enough time to update the property.
He thinks the release is happening before the the property update.
I'm going to try implementing his suggestions, as they can't hurt, but I'd like to hear any other thoughts on the problem.
This is actually a simpler problem than may think.The issue is in the following section of code:
prevValue = Number(scriptProperties.getProperty(propId));
scriptProperties.setProperty(propId, prevValue + 1);
newValue = Number(scriptProperties.getProperty(propId));
You set the property to prevValue + 1, then immediately assign newValue to that same property. Within your code these two lines execute synchronously, but the setProperty method is essentially asynchronous in nature. In infrequent circumstances, there may be a delay in writing to PropertiesService on Googles Servers. Meanwhile, your code continues executing. This can cause newValue to be assigned before propId has actually been updated in the previous line.
It's a trivial solution to fix this:
function isTimeUp(startTime, milliSeconds){
var now = new Date();
return now.getTime() - startTime.getTime() > milliSeconds
}
.
.
.
prevValue = Number(scriptProperties.getProperty(propId));
newValue = PrevValue + 1;
scriptProperties.setProperty(propId, newValue);
var start = new Date();
// run while loop for a maximum 30000 milliseconds = 30 seconds, to prevent endless loop
while (Number(scriptProperties.getProperty(propId))) != newValue &&
!isTimeUp(start, 30000)){};
.
.
.
Assign newValue directly from prevValue within your code, rather than from the property. No more asynchronous issue.
The while statement and isTimeUp() function are to make sure the property is updated before releasing the lock. That may be a bit of overkill, but you leave the function certain that propId is properly updated before the next user runs it, and the isTimeUp() function ensures no endless loop in the event of an internet glitch writing to PropertiesService (If I wasn't getting lazy towards the end writing this up, I would have also rolled back any effects if the update timed out. But now we're starting to go down the rabbit hole ;)

How to apply expiry times to keys in HTML5 indexeddb?

In indexedDB in html5 api, I can use it to store key-value pairs. But how can I make sure that after adding a certain key-value, after 1 day, that key should automatically be deleted from the db.
I was thinking of wrapping the value in an object with current datetime, and expiry time, and when u get the value, check the time difference, but is this the best way?
Thanks
Yep, what Scott Marcus and dgrogan said. One more hint: if you create an index on the timestamp, you can iterate a cursor over the range of "expired" values and delete them after opening the database.
const open = indexedDB.open("demo");
open.onupgradeneeded = function () {
const db = open.result;
const store = db.createObjectStore("store");
const index = store.createIndex("timestamp", "timestamp");
// Populate with some dummy data, with about half from the past:
for (let id = 0; id < 20; ++id) {
store.put(
{
value: Math.random(),
timestamp: new Date(Date.now() + (Math.random() - 0.5) * 10000),
},
id
);
}
};
open.onsuccess = function () {
const db = open.result;
const tx = db.transaction("store", "readwrite");
// Anything in the past:
const range = IDBKeyRange.upperBound(new Date());
tx
.objectStore("store")
.index("timestamp")
.openCursor(range).onsuccess = function (e) {
const cursor = e.target.result;
if (!cursor) return;
console.log("deleting: " + cursor.key);
cursor.delete();
cursor.continue();
};
// This transaction will run after the first commits since
// it has overlapping scope:
const tx2 = db.transaction("store");
tx2.objectStore("store").count().onsuccess = function (e) {
console.log("records remaining: " + e.target.result);
};
};
Support for automatically expiring IndexedDB and other storage data is being considered. See https://github.com/whatwg/storage/issues/11. It would be helpful if you could describe your use case there.
In the meantime you'll have to do something like you outlined or what Scott Marcus suggests.
IndexedDB can only be modified by code. It has no automatic capabilities. Why not just store the data with a timestamp value and modify your code to remove it when the timestamp is out of date?
I wrote a npm module sometime back which expires indexedDB keys after x mins. Also it has very simple api (similar to memcache/localstorage) and is suited for use as a key-value store.
You can check it on npm here .

Select specific data in JSON output

I've created a function that does a http request and then saves some data from the JSON output.
$scope.addMovie = function() {
'http://api.themoviedb.org/3/movie/206647?api_key=a8f7039633f2065942cd8a28d7cadad4&append_to_response=releases'
// Search for release dates using the ID.
var base = 'http://api.themoviedb.org/3/movie/';
var movieID = $(event.currentTarget).parent().find('.movieID').text()
var apiKey = 'a8f7039633f2065942cd8a28d7cadad4&query='
var append_to_response = '&append_to_response=releases'
var callback = 'JSON_CALLBACK'; // provided by angular.js
var url = base + movieID + '?api_key=' + apiKey + append_to_response + '&callback=' + callback;
$http.jsonp(url,{ cache: true}).
success(function(data, status, headers, config) {
if (status == 200) {
// $scope.movieListID = data.results;
$scope.movieListID = data;
console.log($scope.movieListID);
createMovie.create({
title: $scope.movieListID.original_title,
release_date: $scope.movieListID.release_date,
image: $scope.movieListID.poster_path
}).then(init);
} else {
console.error('Error happened while getting the movie list.')
}
})
};
This function saves the title, release date en posterpath and that works fine. The problem is that it only saves one release_date while the JSON output has a lot more, but I don't know how to acces that.
This is a example of the JSON output I request
It has a release_date, which I save now, but it also has more information,
releases":{
"countries":[
{"certification":"","iso_3166_1":"GB","primary":true,"release_date":"2015-10-26"},
{"certification":"","iso_3166_1":"US","primary":false,"release_date":"2015-11-06"},
{"certification":"","iso_3166_1":"NL","primary":false,"release_date":"2015-11-05"},
{"certification":"","iso_3166_1":"BR","primary":false,"release_date":"2015-11-05"},
{"certification":"","iso_3166_1":"SE","primary":false,"release_date":"2015-11-04"},
{"certification":"","iso_3166_1":"IE","primary":false,"release_date":"2015-10-26"},
How would I go about saving the release date for the NL release?
You just need to iterate through the countries array, and check if the country code matches the one you wish to retrieve. For your example with 'NL':
var releaseNL;
for (var i = 0; i < $scope.movieList.releases.countries.length; i++) {
var release = $scope.movieList.releases.countries[i];
if (release['iso_3166_1'] == 'NL') {
releaseNL = release;
}
}
This is just one of many ways to do this (e.g. you could use angular.forEach, wrap it inside a function, etc.), but this should give you an idea.
Remark: I noticed you have been asking a lot of very basic questions today, which you could easily answer yourself with a bit more research. E.g. this question is not even AngularJS related, but just a simple JavaScript task. So maybe try to show a bit more initiative next time! ;)

Geolocation feedback while accepting the request

the geolocation implementation is quite good and got few steps to observe but only on thing is missing, i guess.
Im not able to see if the user accepted the request or not ( before i get the position object ), i dunno if the user just ignores my request ( during my timeout ) or if the request just get lost ( and the failure callback doesnt get called for no reason ).
It would be useful to set a timestamp when the user accepts the request, i couldnt find anything which gives me that kind of response.
Based on my new understanding of what you are after, you want something like this.
(Tested: in Opera - works, Firefox 3.6 & Chrome 8 - not so much (I need more time to debug))
Scenario:
Page attempts to get location... but user ignores the prompt completely thus there is no (accept or deny) and since the request for the location is never sent, there is no timeout either!
Based on this you may want to add your own logic to handle this scenario. For the sake of this example, I'm going to prototype my own "wrapper" method. (for the picky - I'm not condoning using globals etc. I was just trying to get something to work)
navigator.geolocation.requestCurrentPosition = function(successCB, errorCB, timeoutCB, timeoutThreshold, options){
var successHandler = successCB;
var errorHandler = errorCB;
window.geolocationTimeoutHandler = function(){
timeoutCB();
}
if(typeof(geolocationRequestTimeoutHandler) != 'undefined'){
clearTimeout(window['geolocationRequestTimeoutHandler']);//clear any previous timers
}
var timeout = timeoutThreshold || 30000;//30 seconds
window['geolocationRequestTimeoutHandler'] = setTimeout('geolocationTimeoutHandler()', timeout);//set timeout handler
navigator.geolocation.getCurrentPosition(
function(position){
clearTimeout(window['geolocationRequestTimeoutHandler']);
successHandler(position);
},
function(error){
clearTimeout(window['geolocationRequestTimeoutHandler']);
errorHandler(error);
},
options
);
};
function timeoutCallback(){
alert('Hi there! we are trying to locate you but you have not answered the security question yet.\n\nPlease choose "Share My Location" to enable us to find you.');
}
function successCallback(position){
var msg = '';
msg += 'Success! you are at: ';
msg += '\nLatitude: ' + position.coords.latitude;
msg += '\nLongitude: ' + position.coords.longitude;
msg += '\nAltitude: ' + position.coords.altitude;
msg += '\nAccuracy: ' + position.coords.accuracy;
msg += '\nHeading: ' + position.coords.heading;
msg += '\nSpeed: ' + position.coords.speed;
alert(msg);
}
function errorCallback(error){
if(error.PERMISSION_DENIED){
alert("User denied access!");
} else if(error.POSITION_UNAVAILABLE){
alert("You must be hiding in Area 51!");
} else if(error.TIMEOUT){
alert("hmmm we timed out trying to find where you are hiding!");
}
}
navigator.geolocation.requestCurrentPosition(successCallback, errorCallback, timeoutCallback, 7000, {maximumAge:10000, timeout:0});
The concept is to set up a timer first (defaults to 30 seconds if not set). If the user doesn't do anything before the timer expires, a timeoutCallback is called.
Notes:
Some UI's (e.g. iPhone/iPad/iPod Safari) may make the Allow/Deny prompt modal - thus the user can't really continue until they pick something (I'd suggest to leave these users alone and let the default UI handle things
If the user Allows the request (late), the timeout may still fire before the response comes back - I don't think there is anything you can do about this
Code above is an example only... it needs cleaning up.
It is part of the Geolocation API:
// navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options);
navigator.geolocation.getCurrentPosition(
function(position){
//do something with position;
}, function(){
//handle condition where position is not available
//more specifically you can check the error code...
//error.code == 1
if(error.PERMISSION_DENIED){
alert("you denied me! ");
}
});
If you specify the errorCallback... then you can track if the user has declined to provide access.
Possible error codes include:
error.PERMISSION_DENIED (numeric value 1)
error.POSITION_UNAVAILABLE (numeric value 2)
error.TIMEOUT (numeric value 3)
Tested it successful in FF 3.5, Opera 10.6, Chrome8, IE6-8..
var succeed = function(obj) {
navigator.geolocation.received = true;
!navigator.geolocation.timedout?alert('GOT YAH'):alert('GOT YAH but user was to slow');
};
var failed = function(obj) {
navigator.geolocation.received = true;
!navigator.geolocation.timedout?alert('just failed'):alert('failed and user was to slow as well, tzz ._.');
};
var timedout = function() {
navigator.geolocation.timedout = true; // could be used for other callbacks to trace if its timed out or not
!navigator.geolocation.received?alert('Request timed out'):null;
}
// Extend geolocation object
if ( navigator.geolocation ) {
navigator.geolocation.retrievePermission = function retrievePermission(succeed,failed,options,timeout) {
this.received = false; // reference for timeout callback
this.timedout = false; // reference for other callbacks
this.getCurrentPosition.apply(this,arguments); // actual request
// Trigger timeout with its function; default timeout offset 5000ms
if ( timeout ) {
setTimeout(timeout.callback,timeout.offset || 5000);
}
}
// New location request with timeout callback
navigator.geolocation.retrievePermission(succeed,failed,{},{
offset: 10000, // miliseconds
callback: timedout
});
// Awesome thingy is not implemented
} else {
alert('geolocation is not supported');
}
With that workaround we know if the request timedout, even when the succeess / failure callback get called afterwards.

Does HTML5 localStorage have an expiry date? [duplicate]

For how long is data stored in localStorage (as part of DOM Storage in HTML5) available? Can I set an expiration time for the data which I put into local storage?
I would suggest to store timestamp in the object you store in the localStorage
var object = {value: "value", timestamp: new Date().getTime()}
localStorage.setItem("key", JSON.stringify(object));
You can parse the object, get the timestamp and compare with the current Date, and if necessary, update the value of the object.
var object = JSON.parse(localStorage.getItem("key")),
dateString = object.timestamp,
now = new Date().getTime().toString();
compareTime(dateString, now); //to implement
Alternatively, you could use a light-weight wrapper like localstorage-slim.js which handles this for you.
It's not possible to specify expiration. It's completely up to the user.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
Of course, it's possible that something your application stores on the client may not be there later. The user can explicitly get rid of local storage, or the browser may run into space considerations. It's good to program defensively. Generally however things remain "forever" based on some practical definition of that word.
edit — obviously, your own application can actively remove stuff if it decides it's too old. That is, you can explicitly include some sort of timestamp in what you've got saved, and then use that later to decide whether or not information should be flushed.
You can use lscache. It handles this for you automatically, including instances where the storage size exceeds the limit. If that happens, it begins pruning items that are the closest to their specified expiration.
From the readme:
lscache.set
Stores the value in localStorage. Expires after specified number of minutes.
Arguments
key (string)
value (Object|string)
time (number: optional)
This is the only real difference between the regular storage methods. Get, remove, etc work the same.
If you don't need that much functionality, you can simply store a time stamp with the value (via JSON) and check it for expiry.
Noteworthy, there's a good reason why local storage is left up to the user. But, things like lscache do come in handy when you need to store extremely temporary data.
Brynner Ferreira, has brought a good point: storing a sibling key where expiration info resides. This way, if you have a large amount of keys, or if your values are large Json objects, you don't need to parse them to access the timestamp.
here follows an improved version:
/* removeStorage: removes a key from localStorage and its sibling expiracy key
params:
key <string> : localStorage key to remove
returns:
<boolean> : telling if operation succeeded
*/
function removeStorage(name) {
try {
localStorage.removeItem(name);
localStorage.removeItem(name + '_expiresIn');
} catch(e) {
console.log('removeStorage: Error removing key ['+ key + '] from localStorage: ' + JSON.stringify(e) );
return false;
}
return true;
}
/* getStorage: retrieves a key from localStorage previously set with setStorage().
params:
key <string> : localStorage key
returns:
<string> : value of localStorage key
null : in case of expired key or failure
*/
function getStorage(key) {
var now = Date.now(); //epoch time, lets deal only with integer
// set expiration for storage
var expiresIn = localStorage.getItem(key+'_expiresIn');
if (expiresIn===undefined || expiresIn===null) { expiresIn = 0; }
if (expiresIn < now) {// Expired
removeStorage(key);
return null;
} else {
try {
var value = localStorage.getItem(key);
return value;
} catch(e) {
console.log('getStorage: Error reading key ['+ key + '] from localStorage: ' + JSON.stringify(e) );
return null;
}
}
}
/* setStorage: writes a key into localStorage setting a expire time
params:
key <string> : localStorage key
value <string> : localStorage value
expires <number> : number of seconds from now to expire the key
returns:
<boolean> : telling if operation succeeded
*/
function setStorage(key, value, expires) {
if (expires===undefined || expires===null) {
expires = (24*60*60); // default: seconds for 1 day
} else {
expires = Math.abs(expires); //make sure it's positive
}
var now = Date.now(); //millisecs since epoch time, lets deal only with integer
var schedule = now + expires*1000;
try {
localStorage.setItem(key, value);
localStorage.setItem(key + '_expiresIn', schedule);
} catch(e) {
console.log('setStorage: Error setting key ['+ key + '] in localStorage: ' + JSON.stringify(e) );
return false;
}
return true;
}
Here highly recommended to use sessionStorage
it is same as localStorage but destroy when session destroyed / browser close
also localStorage can share between tabs while sessionStorage can use in current tab only, but value does not change on refresh page or change the page
sessionStorage is also useful to reduce network traffic against cookie
for set value use
sessionStorage.setItem("key","my value");
for get value use
var value = sessionStorage.getItem("key");
click here for view api
all ways for set are
sessionStorage.key = "my val";
sessionStorage["key"] = "my val";
sessionStorage.setItem("key","my value");
all ways for get are
var value = sessionStorage.key;
var value = sessionStorage["key"];
var value = sessionStorage.getItem("key");
While local storage does not supply an expiration mechanism, cookies do. Simply pairing a local storage key with a cookie provides an easy way to ensure that local storage can be updated with the same expiration parameters as a cookie.
Example in jQuery:
if (!$.cookie('your_key') || !localStorage.getItem('your_key')) {
//get your_data from server, then...
localStorage.setItem('your_key', 'your_data' );
$.cookie('your_key', 1);
} else {
var your_data = localStorage.getItem('your_key');
}
// do stuff with your_data
This example sets a cookie with the default parameter to expire when the browser is closed. Thus, when the browser is closed and re-opened, the local data store for your_data gets refreshed by a server-side call.
Note that this is not exactly the same as removing the local data store, it is instead updating the local data store whenever the cookie expires. However, if your main goal is to be able to store more than 4K client-side (the limitation for cookie size), this pairing of cookie and local storage will help you to accomplish a larger storage size using the same expiration parameters as a cookie.
The lifecycle is controlled by the application/user.
From the standard:
User agents should expire data from the local storage areas only for security reasons or when requested to do so by the user. User agents should always avoid deleting data while a script that could access that data is running.
From the W3C draft:
User agents should expire data from the local storage areas only for security reasons or when requested to do so by the user. User agents should always avoid deleting data while a script that could access that data is running.
You'll want to do your updates on your schedule using setItem(key, value); that will either add or update the given key with the new data.
// Functions
function removeHtmlStorage(name) {
localStorage.removeItem(name);
localStorage.removeItem(name+'_time');
}
function setHtmlStorage(name, value, expires) {
if (expires==undefined || expires=='null') { var expires = 3600; } // default: 1h
var date = new Date();
var schedule = Math.round((date.setSeconds(date.getSeconds()+expires))/1000);
localStorage.setItem(name, value);
localStorage.setItem(name+'_time', schedule);
}
function statusHtmlStorage(name) {
var date = new Date();
var current = Math.round(+date/1000);
// Get Schedule
var stored_time = localStorage.getItem(name+'_time');
if (stored_time==undefined || stored_time=='null') { var stored_time = 0; }
// Expired
if (stored_time < current) {
// Remove
removeHtmlStorage(name);
return 0;
} else {
return 1;
}
}
// Status
var cache_status = statusHtmlStorage('cache_name');
// Has Data
if (cache_status == 1) {
// Get Cache
var data = localStorage.getItem('cache_name');
alert(data);
// Expired or Empty Cache
} else {
// Get Data
var data = 'Pay in cash :)';
alert(data);
// Set Cache (30 seconds)
if (cache) { setHtmlStorage('cache_name', data, 30); }
}
If you’re familiar with the browsers locaStorage object, you know that there’s no provision for providing an expiry time. However, we can use Javascript to add a TTL (Time to live) to invalidate items in locaStorage after a certain period of time elapses.
function setLocalStorageItem(key, value, ttl) {
// `item` is an object which contains the original value
// as well as the time when it's supposed to expire
let item = {
value: value,
expiry: ttl ? Date.now() + ttl : null
};
localStorage.setItem(key, JSON.stringify(item));
}
function getLocalStorageItem(key) {
let item = localStorage.getItem(key);
// if the item doesn't exist, return null
if (!item) return null;
item = JSON.parse(item);
// compare the expiry time of the item with the current time
if (item.expiry && Date.now() > item.expiry) {
// If the item is expired, delete the item from storage and return null
localStorage.removeItem(key);
return null;
}
return item.value;
}
You can try this one.
var hours = 24; // Reset when storage is more than 24hours
var now = Date.now();
var setupTime = localStorage.getItem('setupTime');
if (setupTime == null) {
localStorage.setItem('setupTime', now)
} else if (now - setupTime > hours*60*60*1000) {
localStorage.clear()
localStorage.setItem('setupTime', now);
}
If anyone still looking for a quick solution and don't want dependencies like jquery etc I wrote a mini lib that add expiration to local / session / custom storage, you can find it with source here:
https://github.com/RonenNess/ExpiredStorage
If someone using jStorage Plugin of jQuery the it can be add expiry with setTTL function if jStorage plugin
$.jStorage.set('myLocalVar', "some value");
$.jStorage.setTTL("myLocalVar", 24*60*60*1000); // 24 Hr.
Javascript localStorage do not have any options to set expire time
Then i use these functions to check supports, Set and Get
function ls_support(){
return "localStorage" in window&&window["localStorage"]!==null;
}
function lsset(key,val,exp){
if(ls_support()){
if(!exp) exp=600;// = 10 minutes Default
localStorage[key]=
JSON.stringify({
"val":val,
"exp":~~((new Date()).getTime()/1000)+exp
});
}
}
function lsget(key){
if(ls_support()){
str=localStorage[key];
if("undefined"!=typeof(str)&&str.length){
try{// is json or not
json=JSON.parse(str);
}catch(e){// if variable not set via lsset func
//json.exp=false;// will return null
return str;// will return original variable
}
if(json.exp){// variable setted via lsset func
if(~~((new Date()).getTime()/1000)>json.exp){// expired
delete localStorage[key];
}else{
return json.val;
}
}
}
}
return null;
}
And it seems works fine :
Workaround using angular and localforage:
angular.module('app').service('cacheService', function() {
return {
set: function(key, value, expireTimeInSeconds) {
return localforage.setItem(key, {
data: value,
timestamp: new Date().getTime(),
expireTimeInMilliseconds: expireTimeInSeconds * 1000
})
},
get: function(key) {
return localforage.getItem(key).then(function(item) {
if(!item || new Date().getTime() > (item.timestamp + item.expireTimeInMilliseconds)) {
return null
} else {
return item.data
}
})
}
}
})
#sebarmeli's approach is the best in my opinion, but if you only want data to persist for the life of a session then sessionStorage is probably a better option:
This is a global object (sessionStorage) that maintains a storage area
that's available for the duration of the page session. A page session
lasts for as long as the browser is open and survives over page
reloads and restores. Opening a page in a new tab or window will cause
a new session to be initiated.
MDN: sessionStorage
For the benefit of searchers:
Like Fernando, I didn't want to add a load of json when the values stored were simple. I just needed to track some UI interaction and keep the data relevant (e.g. how a user used an ecommerce site before checking out).
This will not meet everyones criteria, but will hopefully be a quick copy+paste starter for someone and save adding another lib.
NOTE: This would not be good if you need to retrieve the items individually.
// Addition
if(window.localStorage){
localStorage.setItem('myapp-' + new Date().getTime(), 'my value');
}
// Removal of all expired items
if(window.localStorage){
// two mins - (1000 * 60 * 20) would be 20 mins
var expiryTime = new Date().getTime() - (1000 * 60 * 2);
var deleteRows = [];
for(var i=0; i < localStorage.length; i++){
var key = localStorage.key(i);
var partsArray = key.split('-');
// The last value will be a timestamp
var lastRow = partsArray[partsArray.length - 1];
if(lastRow && parseInt(lastRow) < expiryTime){
deleteRows.push(key);
}
}
// delete old data
for(var j=0; j < deleteRows.length; j++){
localStorage.removeItem(deleteRows[j]);
}
}
function setStorage(name,value){
return localStorage.setItem(name,JSON.stringify({value:value,timestamp:Math.round((new Date()).getTime()/1000)}));
}
function getStorage(name,timeout){
var object = JSON.parse(localStorage.getItem(name));
if(object){
if(Math.round((new Date()).getTime()/1000) < (object.timestamp+timeout)){
return object.value;
}else{
localStorage.removeItem(name);
}
}
return false;
}