I currently have some code using jQuery Deferred and ajax to call remote api and get the data and put in localStorage and get data from localStorage.
This code seems buggy the first time it runs (i.e. it doesn’t render the data until browser refresh) but it works perfectly after that until the cache expires and same problem again the first time it runs.
I want to rewrite the code using promise to modernize the codebase as well as tackle the bug. I am pretty new to JS promises and I wonder if there is any other improvements that can be made?
var btCalendarOps = { lifespan: 4*3600000, //4 hours = 8*3600000 def: $.Deferred(function(def){ if (localStorage && localStorage.getItem('btCalData')) { def.resolve(JSON.parse(localStorage.getItem('btCalData'))) } else { def.resolve({}); } }).promise(), fetchData: function(){ if (localStorage && (!localStorage.getItem('btCalData') || !localStorage.getItem('btCalDataTimeSpan') || parseInt($.now(), 10) - parseInt(localStorage.getItem('btCalDataTimeSpan'), 10) > this.lifespan)) { console.log("localStorage refreshed with fresh API data"); this.def = $.ajax({ url: url, method: "GET", dataType: 'json', success: function (data) { if (localStorage) { localStorage.setItem('btCalData', JSON.stringify(data)); } } }).then(function(){ return JSON.parse(localStorage.getItem('btCalData')); }); localStorage.setItem('btCalDataTimeSpan', $.now()); } return this.def; } }
//current usage
btCalendarOps.fetchData().done(function (data) { //render data as a list });
My new version:
const btCalendarOps = { lifespan: 4*3600000, //4 hours = 8*3600000 fetchData: function(){ const cache = localStorage && localStorage.getItem('btCalData') && localStorage.getItem('btCalDataTimeSpan') && parseInt($.now(), 10) - parseInt(localStorage.getItem('btCalDataTimeSpan'), 10) > this.lifespan ? localStorage.getItem('btCalData') : null; // if the data is in the cache, return it. if (cache){ return Promise.resolve(JSON.parse(cache)); } // else get the data and store it. return Promise.resolve( fetch(url) .then((res) => res.json()) .then((data) => { localStorage && localStorage.setItem('btCalData', JSON.stringify(data));localStorage.setItem('btCalDataTimeSpan', $.now()); return data; }) ); } }
Advertisement
Answer
You could use an async
function:
const btCalendarOps = { lifespan: 4*3600000, //4 hours = 8*3600000 async fetchData() { if (!localStorage) return {}; if (!localStorage.getItem('btCalData') || !localStorage.getItem('btCalDataTimeSpan') || Date.now() - localStorage.getItem('btCalDataTimeSpan') > this.lifespan) { console.log("localStorage to be refreshed with fresh API data"); localStorage.setItem('btCalDataTimeSpan', Data.now()); const data = await (await fetch(url)).json(); localStorage.setItem('btCalData', JSON.stringify(data)); } const json = localStorage.getItem('btCalData'); return json ? JSON.parse(json) : {}; } }
You should no longer use done
, but then
(or async
and await
):
btCalendarOps.fetchData().then(function (data) { //render data as a list });