Caching for promises.
npm install promise-cachify
- Concurrent de-duplication.
- Caching resolved values while ignore rejected.
- Data persistance.
- Data isolation(fresh new data for each return).
- Strong and type-safe cache key generation.
- Customizable (key generation, exipre time... )
- Deletion of cached items.
- Debug mode
- Support two styles of usage.
- Fully typescript-ready.
- No dependencies
.do style (recommended)
import withCache, { setDefaults, DefaultKey } from 'promise-cachify';
const getDetail = withCache(function (id: number) {
return yourFetchFn('/api/getDetail', { id });
});
await Promise.all([getDetail.do(1), getDetail.do(1), getDetail.do(1)]);
// concurrent request share the same http request.
// so the above results in just 1 http call;
// note the '.do(...)', that explicitly tell the reader
// "hi man, the result is probably from cache! "
await getDetail.do(1); // from cache;
as-it-is style
import { cache, setDefaults, DefaultKey } from 'promise-cachify';
const getDetail = cache(function (id: number) {
return yourFetchFn('/api/getDetail', { id });
});
await Promise.all([getDetail(1), getDetail(1), getDetail(1)]);
await getDetail(1); // from cache;
APIs of Both styles are with the same signature. But APIs are under getDetail.cache
when as-it-is style.
style | API signature | API location | Suitable scene |
---|---|---|---|
.do | same | fn. | new code |
as-it-is | same | fn.cache. | old project |
// clear all cached data for every 'id';
getDetail.clearAll();
// clear cached data for id=1
getDetail.clear(getDetail.getCacheKey(1));
// or the following, if you know how the cache key is generated;
getDetail.clear('$-1');
getDetail.do(1); // from server;
Most of time you don't need to do this, but this is needed when your params is complex.
const getDetail = withCache(
function (id: number) {
return yourFetchFn('/api/getDetail', { id });
},
{
// same signature as the first argument of withCache
key: (id) => String(id),
// key: 'or_your_static_key',
}
);
const getDetail = withCache(
function (id: number) {
return yourFetchFn('/api/getDetail', { id });
},
{
persist: 'your_global_unique_key',
// persistMedia: 'sessionStorage', // default. Another available option is "localStorage"
}
);
// this will respect to `expire` policy
const getDetail = withCache(
function (id: number) {
return yourFetchFn('/api/getDetail', { id });
},
{ debug: true }
);
// track cache behavior.
const getDetail = withCache(
function (id: number) {
return yourFetchFn('/api/getDetail', { id });
},
{
persist: 'CRM_USER_GETDETIAL',
persistMedia: 'sessionStorage',
maxAge: 2, // expire after 2 seconds
debug: true,
key: (id) => String(id),
}
);
do(...args: TArgs): Promise<TOut>
getCacheKey(...args: TArgs): string | null;
clear(key?: TKey): void;
clearAll(): void;
set(data: TOut | Promise<TOut>, key?: TKey): boolean;
get(key?: TKey): Promise<TOut> | null;
getAll(): Map<string, ICacheItem>;
has(key?: TKey): boolean;
auto-key-generation only supports signature with the following constraints;
type TValue = string | number | boolean;
fn(...args: ({ [key: string]: TValue } | TValue | TValue[])[])
- arguments that not satisfy the above constraints will cause no cache.
- the keys of an object will be sorted.
- none-string value will be prefixed with
$-
- use default key if no arguments or one argument with
undefined
value
examples
signature | key |
---|---|
fn({ id: 1, name: 'xx', age: 1 }) | 'age=$-1&id=$-1&name=xx' |
fn({ name: 'xx', age: 1, id: 1 }) | 'age=$-1&id=$-1&name=xx' |
fn('1', [1, '2'], { id: 1, name: null }) | '1&[$-1_2]&id=$-1&name=$-null' |
fn({ id: 1 }, { id: 2 }) | 'id=$-1&id=$-2' |
fn([1], ['2']) | '[$-1]&[2]' |
fn({}) | '{}' |
fn(null) | '$-null' |
fn('null') | 'null' |
fn() or fn(undefined) | '__INTERNAL_USE__' |
fn({ id: 1, d: { name: 'el' } }) | null and no cache will be performed |
see more at the first test case in __test__/index.test.ts
browsers that support or polyfilled Map
Promise
MIT