I have an app that makes a lot of asynchronous fetch
calls, some of which are identical.
I have a function that supersets fetch
(say fetchPlus
), by creating a pseudo-unique identifier per request based on the arguments. That way, I can store the result in sessionStorage
and access it.
function fetchCacheStore(hash) {
const storeItem = 'fetch_' + hash;
return {
getCache: function () {
return JSON.parse(sessionStorage.getItem(storeItem));
},
setCache: function (data) {
sessionStorage.setItem(storeItem, JSON.stringify(data));
setTimeout(function () { sessionStorage.removeItem(storeItem); }, 25); // Clear the cache item shortly after
},
};
}
function fetchPlus() {
const stringHasher = function (s) { // Adapted from https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript/22429679#comment94234739_7616484
for (var i = h = 0; i < s.length; i++) {
h = Math.imul(31, h) + s.charCodeAt(i) | 0;
}
return btoa(h);
}
let thisCallDetails = JSON.stringify(Array.prototype.slice.call(arguments).sort());
let fetchCallHash = stringHasher(thisCallDetails);
let fetchCache = fetchCacheStore(fetchCallHash);
let fetchCacheGet = fetchCache.getCache();
let promise;
if (fetchCacheGet === null) { // The data is not cached
promise = fetch(...arguments); // Create the fetch call
promise.then(data => {
data.close.json().then(content => {
fetchCache.setCache(content);
});
}); // Store the result in the cache
} else {
let dataHeaders = { "status": 200, "Content-Type": "application/json" };
promise = new Response(fetchCacheGet, dataHeaders); // Programatically create a Response
}
return promise;
}
Everything works well aside from the fact that when the data exists in sessionStorage
, I am returning a JSON object directly, and not a Response
, so in my code, when I do a call like so:
fetchPlus(url, params)
.then(response => response.json())
.then(data => …)
I end up with an error letting me know that I can't run json()
on response
.
The line promise = new Response(fetchCacheGet, dataHeaders);
is probably incorrect, but I am not sure how to "reverse" the data into being the data spit out from the original fetch
call. Maybe I'm missing something obvious. Or maybe this is all wrong.
I'm open to suggestions but this app is already set up so removing all the .then(response => response.json())
from the codebase is not an option.
Also, I am aware my code isn't the best in class, so do forgive me. Once again, open to suggestions as long as it's constructive.
I'd love help to make this work if anyone has a few minutes to spare.
UPDATE: Functioning code
Thanks to @AuxTaxo's answer below, I've solved my issue. For anyone interested, here is the updated code:
function fetchCacheStore(hash) {
const storeItem = 'fetch_' + hash;
return {
getCache: function () {
return sessionStorage.getItem(storeItem);
},
setCache: function (data) {
sessionStorage.setItem(storeItem, data);
setTimeout(function () { sessionStorage.removeItem(storeItem); }, 1000); // Clear the cache item after a short while
},
};
}
function fetchPlus() {
const stringHasher = function (s) { // Adapted from https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript/22429679#comment94234739_7616484
for (var i = h = 0; i < s.length; i++) {
h = Math.imul(31, h) + s.charCodeAt(i) | 0;
}
return btoa(h);
}
let thisCallDetails = JSON.stringify(Array.prototype.slice.call(arguments).sort());
let fetchCallHash = stringHasher(thisCallDetails);
let fetchCache = fetchCacheStore(fetchCallHash);
let fetchCacheGet = fetchCache.getCache();
let promise;
if (fetchCacheGet === null) { // The data is not cached
promise = fetch(...arguments); // Create the fetch call
promise.then(data => {
data.clone().text().then(content => {
fetchCache.setCache(content) // Store the result in the cache
});
});
} else {
let dataHeaders = { "status": 200, headers: { "Content-Type": "application/json" } };
// Programatically create a Response object, which works as a Promise
promise = Promise.race([new Response(fetchCacheGet, dataHeaders)]);
}
return promise;
}
// Used as: `fetchPlus(url, params).then(response => response.json()).then(data => { /* … */ })`*