_Go back to the [main documentation page.](../readme.md#api)_ # CachedFunction(key, options) You can think of `CachedFunction` as an advanced "memoize" function that you can call with any arguments, but also: - verify whether a specific set of arguments is cached (`.isCached()`) - only get the cached value if it exists (`.getCached()`) - only get the fresh value, skipping the cache (but still caching the result) (`.getFresh()`) - delete a cached value (`.delete()`) ## key Type: string The unique name that will be used in `chrome.storage.local` combined with the function arguments, like `cache:${key}:{arguments}`. For example, these two calls: ```js const pages = new CachedFunction('pages', {updater: fetchText}); await pages.get(); await pages.get('./contacts'); await pages.get('./about', 2); await pages.get(); // Will be retrieved from cache ``` Will call `fetchText` 3 times and create 3 items in the storage: ```json { "cache:pages": "You're on the homepage", "cache:pages:[\"./contacts\"]": "You're on the contacts page", "cache:pages:[\"./about\",2]": "You're on the about page" } ``` ## options ### updater Required.
Type: `async function` that returns a cacheable value. Returning `undefined` will delete the item from the cache. ### maxAge Type: [`TimeDescriptor`](https://github.com/sindresorhus/to-milliseconds#input)
Default: `{days: 30}` The amount of time after which the cache item will expire after being each cache update. ### staleWhileRevalidate Type: [`TimeDescriptor`](https://github.com/sindresorhus/to-milliseconds#input)
Default: `{days: 0}` (disabled) Specifies how much longer an item should be kept in cache after its expiration. During this extra time, the item will still be served from cache instantly, but `updater` will be also called asynchronously to update the cache. A later call will return the updated and fresher item. ```js const operate = new CachedFunction('posts', { updater: operate, maxAge: { days: 10, }, staleWhileRevalidate: { days: 2, }, }); await operate.get(); // It will run `operate` and cache it for 10 days await operate.get(); // It will return the cache /* 3 days later, cache is expired, but still there */ await operate.get(); // It will return the cache // Asynchronously, it will also run `operate` and cache the new value for 10 more days /* 13 days later, cache is expired and deleted */ await operate.get(); // It will run `operate` and cache it for 10 days ``` ### shouldRevalidate Type: `(cachedValue) => boolean`
Default: `() => false` You may want to have additional checks on the cached value, for example after updating its format. ```js async function getContent(url) { const response = await fetch(url); return response.json(); // For example, you used to return plain text, now you return a JSON object } const content = new CachedFunction('content', { updater: getContent, // If it's a string, it's in the old format and a new value will be fetched and cached shouldRevalidate: cachedValue => typeof cachedValue === 'string', }); const json = await content.get('https://google.com'); // Even if it's cached as a regular string, the cache will be discarded and `getContent` will be called again ``` ### cacheKey Type: `(args: any[]) => string` Default: `JSON.stringify` By default, the function’s `arguments` JSON-stringified array will be used to create the cache key. ```js const posts = new CachedFunction('posts', {updater: fetchPosts}); const user = {id: 123, name: 'Fregante'}; await posts.get(user); // Its result will be stored in the key 'cache:fetchPosts:[{"id":123,name:"Fregante"}]' ``` You can pass a `cacheKey` function to customize how the key is generated, saving storage and making it more sensible: ```js const posts = new CachedFunction('posts', { updater: fetchPosts, cacheKey: (args) => args[0].id, // ✅ Use only the user ID }); const user = {id: 123, name: 'Fregante'}; await posts.get(user); // Its result will be stored in the key 'cache:fetchPosts:123' ``` ## CachedFunction#get(...arguments) This method is equivalent to calling your `updater` function with the specified parameters, unless the result of a previous call is already in the cache: ```js const repositories = new CachedFunction('repositories', {updater: repoApi}); await repositories.get('fregante', 'doma'); // Will call repoApi('fregante', 'doma') await repositories.get('fregante', 'doma'); // Will return the item from the cache await repositories.get('fregante', 'webext-base-css'); // Will call repoApi('fregante', 'webext-base-css') ``` ## CachedFunction#getFresh(...arguments) This updates the cache just like `.get()`, except it always calls `updater` regardless of cache state. It's meant to be used as a "refresh cache" action: ```js const repositories = new CachedFunction('repositories', {updater: repoApi}); await repositories.get('fregante', 'doma'); // Will call repoApi('fregante', 'doma') await repositories.getFresh('fregante', 'doma'); // Will call repoApi('fregante', 'doma') regardless of cache state ``` ## CachedFunction#getCached(...arguments) This only returns the value of a previous `.get()` call with the same arguemnts, but it never calls your `updater`: ```js const repositories = new CachedFunction('repositories', {updater: repoApi}); await repositories.getCached('fregante', 'doma'); // It can be undefined ``` ## CachedFunction#isCached(...arguments) ```js const repositories = new CachedFunction('repositories', {updater: repoApi}); await repositories.isCached('fregante', 'doma'); // => true / false ``` ## CachedFunction#delete(...arguments) ```js const repositories = new CachedFunction('repositories', {updater: repoApi}); await repositories.delete('fregante', 'doma'); ``` ## CachedFunction#applyOverride(arguments, newValue) This method should only be used if you want to override the cache with a custom value, but you should prefer `get` or `getFresh` instead, keeping the logic exclusively in your `updater` function. ```js const repositories = new CachedFunction('repositories', {updater: repoApi}); // Will override the local cache for the `repoApi('fregante', 'doma')` call await repositories.applyOverride(['fregante', 'doma'], {id: 134, lastUpdated: 199837738894}); ``` ## License MIT © [Federico Brigante](https://fregante.com)