Comparing version
{ | ||
"name": "swiss-ak", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"author": "Jack Cannon <jackc@annon.co.uk> (http://c.annon.co.uk/)", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -81,2 +81,24 @@ import { safe } from './safe'; | ||
}; | ||
const getOrRunAsync = async (id: string, orFunc: (id?: string) => T | Promise<T>, expiresIn: ms = getDefaultExpiresIn()): Promise<T> => { | ||
const args = { | ||
id: safe.str(id, false, 'NO-ID'), | ||
orFunc: safe.func(orFunc), | ||
expiresIn: safe.num(expiresIn, true, undefined, undefined, getDefaultExpiresIn()) | ||
}; | ||
try { | ||
// get | ||
const valid = getValidatedValue(args.id); | ||
if (valid.hasValidValue) return valid.value; | ||
// run | ||
const newItem = await args.orFunc(args.id); | ||
storedItems[args.id] = { | ||
expires: Date.now() + args.expiresIn, | ||
value: newItem | ||
}; | ||
return newItem; | ||
} catch (err) { | ||
return undefined as unknown as T; | ||
} | ||
}; | ||
const save = (id: string, item: T, expiresIn: ms = getDefaultExpiresIn()): T => { | ||
@@ -135,2 +157,3 @@ const args = { | ||
getOrRun, | ||
getOrRunAsync, | ||
save, | ||
@@ -262,2 +285,33 @@ remove, | ||
/**<!-- DOCS: cachier.Cachier.getOrRunAsync #### --> | ||
* getOrRunAsync | ||
* | ||
* - `cachier.getOrRunAsync` | ||
* - `cachier.create().getOrRunAsync` | ||
* | ||
* Get a cached item by id, or run an async function to create a new item if it doesn't exist. | ||
* | ||
* The created item will be cached and returned. | ||
* | ||
* Same as `cachier.getOrRun`, but the function can be async. Return will always be a promise. | ||
* | ||
* ```typescript | ||
* const longFn = async (name) => { | ||
* await wait(1000); | ||
* return { name }; | ||
* }; | ||
* | ||
* await cachier.getOrRunAsync('foo', () => longFn('lorem')); // { name: 'lorem' } | ||
* cachier.get('foo'); // { name: 'lorem' } | ||
* | ||
* await cachier.getOrRunAsync('foo', () => longFn('SOMETHING DIFFERENT')); // { name: 'lorem' } | ||
* cachier.get('foo'); // { name: 'lorem' } | ||
* ``` | ||
* @param {string} id | ||
* @param {(id?: string) => T | Promise<T>} orFunc | ||
* @param {ms} [expiresIn=getDefaultExpiresIn()] | ||
* @returns {Promise<T>} | ||
*/ | ||
getOrRunAsync(id: string, orFunc: (id?: string) => T | Promise<T>, expiresIn?: ms): Promise<T>; | ||
/**<!-- DOCS: cachier.Cachier.save #### --> | ||
@@ -264,0 +318,0 @@ * save |
@@ -211,2 +211,95 @@ import * as swissak from '../dist'; | ||
describe('getOrRunAsync', () => { | ||
multiTest( | ||
[ | ||
[swissak.cachier, 'cachier'], | ||
[swissak.cachier.create(), 'cachier.create()'] | ||
], | ||
(cachier, name) => { | ||
it(should` exist as ${name + '.getOrRunAsync'}`, () => { | ||
expect(cachier.getOrRunAsync).toBeDefined(); | ||
}); | ||
it(should` return the item if it exists`, async () => { | ||
const cache = cachier.create(); | ||
cache.save('foo', { name: 'foo' }); | ||
expect(await cache.getOrRunAsync('foo', async () => ({ name: 'BAR' }))).toEqual({ name: 'foo' }); | ||
}); | ||
it(should` run the function if the item does not exist`, async () => { | ||
const cache = cachier.create(); | ||
expect(await cache.getOrRunAsync('foo', async () => ({ name: 'BAR' }))).toEqual({ name: 'BAR' }); | ||
expect(cache.get('foo')).toEqual({ name: 'BAR' }); | ||
}); | ||
it(should` handle async functions correctly`, async () => { | ||
const cache = cachier.create(); | ||
const asyncFn = async () => { | ||
await new Promise((resolve) => setTimeout(resolve, 50)); | ||
return { name: 'ASYNC' }; | ||
}; | ||
expect(await cache.getOrRunAsync('foo', asyncFn)).toEqual({ name: 'ASYNC' }); | ||
expect(cache.get('foo')).toEqual({ name: 'ASYNC' }); | ||
}); | ||
it(should` respect the expiresIn parameter`, async () => { | ||
const cache = cachier.create(); | ||
let runCount = 0; | ||
const getValue = async () => { | ||
runCount++; | ||
return `value${runCount}`; | ||
}; | ||
// Save with 100ms expiry | ||
expect(await cache.getOrRunAsync('temp', getValue, 100)).toBe('value1'); | ||
expect(runCount).toBe(1); | ||
// Should return cached value | ||
expect(await cache.getOrRunAsync('temp', getValue, 100)).toBe('value1'); | ||
expect(runCount).toBe(1); | ||
// Wait 200ms (longer than expiry) | ||
await new Promise((resolve) => setTimeout(resolve, 200)); | ||
// Should run function again after expiry | ||
expect(await cache.getOrRunAsync('temp', getValue, 100)).toBe('value2'); | ||
expect(runCount).toBe(2); | ||
}); | ||
kitchenSink.toEqual( | ||
'id', | ||
async (v: any) => { | ||
const cache = cachier.create(); | ||
cache.save('NO-ID', { name: 'foo' }); | ||
return await cache.getOrRunAsync(v, async () => ({ name: 'BAR' })); | ||
}, | ||
kitchenSink.safe.str(undefined, false, 'NO-ID'), | ||
kitchenSink.samples.general | ||
); | ||
kitchenSink.toEqual( | ||
'orFunc', | ||
async (v: any) => { | ||
const cache = cachier.create(); | ||
cache.save('foo', { name: 'foo' }); | ||
return await cache.getOrRunAsync('foo', v); | ||
}, | ||
kitchenSink.safe.func(undefined), | ||
kitchenSink.samples.general | ||
); | ||
kitchenSink.toEqual( | ||
'expiresIn', | ||
async (v: any) => { | ||
const cache = cachier.create(); | ||
await cache.getOrRunAsync('foo', async () => 'value', v); | ||
return cache.get('foo'); | ||
}, | ||
kitchenSink.safe.num(undefined, true, undefined, undefined, Infinity), | ||
kitchenSink.samples.num | ||
); | ||
} | ||
); | ||
}); | ||
describe('save', () => { | ||
@@ -213,0 +306,0 @@ multiTest( |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
1288414
0.71%30836
0.67%6203
0.63%