Skip to content

Latest commit

 

History

History
195 lines (135 loc) · 6.2 KB

cached-function.md

File metadata and controls

195 lines (135 loc) · 6.2 KB

Go back to the main documentation page.

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:

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:

{
	"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
Default: {days: 30}

The amount of time after which the cache item will expire after being each cache update.

staleWhileRevalidate

Type: TimeDescriptor
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.

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.

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.

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:

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:

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:

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:

const repositories = new CachedFunction('repositories', {updater: repoApi});
await repositories.getCached('fregante', 'doma'); // It can be undefined

CachedFunction#isCached(...arguments)

const repositories = new CachedFunction('repositories', {updater: repoApi});
await repositories.isCached('fregante', 'doma');
// => true / false

CachedFunction#delete(...arguments)

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.

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