localStorage
is not available in a service worker per the specification. The reason is that since it provides synchronous access it has to be read in its entirety before starting the JS environment, which may take some time that's comparable to the startup time (~50ms) of the environment itself in case the storage contains several megabytes (the maximum is 5MB).
Only asynchronous storage API are available in a service worker.
Extensions can use these:
chrome.storage
- good: small amount of simple data
- good: directly available in a content script
- meh: only JSON-compatible types (string, number, boolean, null, object/array consisting of these types recursively), so attempting to store complex things like
Set
or Map
will end up as an empty object {}
and you'll have to serialize them e.g. [...mySet]
when writing, new Set(result.foo)
when reading.
- bad: very slow for big/nested data
IndexedDB
- good: very fast for any amount/complexity of data
- good: more data types like ArrayBuffer, File, Blob, typed arrays, Set, Map,
see the structured clone algorithm
- meh: the data is not available in a content script so you'll have to use messaging
- bad: its API is obsolete and clunky, but there are several libraries that fix it
These API are asynchronous, so you will have to rework your code.
Since Chrome 95 promisified chrome.storage
, we can use async/await for your example.
And don't forget to add "storage"
to "permissions"
in manifest.json.
const LS = chrome.storage.local;
async function pruneStorage() {
const remover = Date.now() - 2500000000;
const key = lastchecked[0];
if ((await LS.get(key))[key] < remover) {
await LS.set({[key]: Date.now()});
} else {
const toRemove = Object.entries(await LS.get())
.map(([k, v]) => v < remover && k)
.filter(Boolean);
if (toRemove.length) {
await LS.remove(toRemove);
}
}
}
Alternatively, mimic localStorage
:
const LS = {
getAllItems: () => chrome.storage.local.get(),
getItem: async key => (await chrome.storage.local.get(key))[key],
setItem: (key, val) => chrome.storage.local.set({[key]: val}),
removeItems: keys => chrome.storage.local.remove(keys),
};
async function pruneStorage() {
const remover = Date.now() - 2500000000;
const key = lastchecked[0];
if (await LS.getItem(key) < remover) {
await LS.setItem(key, Date.now());
} else {
const toRemove = Object.entries(await LS.getAllItems())
.map(([k, v]) => v < remover && k)
.filter(Boolean);
if (toRemove.length) {
await LS.removeItems(toRemove);
}
}
}
Warning: don't make your chrome.runtime.onMessage listener async
if you want to send the response asynchronously, use an async IIFE or a separate function instead, more info.