Skip to content

Commit

Permalink
add cache.forceFetch, a fetch() that cannot return undefined
Browse files Browse the repository at this point in the history
Fix: #329
  • Loading branch information
isaacs committed Jun 27, 2024
1 parent aaaa7d7 commit 01b4c0c
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 3 deletions.
57 changes: 55 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,8 @@ export namespace LRUCache {
* various states.
*
* - inflight: there is another fetch() for this key which is in process
* - get: there is no fetchMethod, so {@link LRUCache#get} was called.
* - get: there is no {@link OptionsBase.fetchMethod}, so
* {@link LRUCache#get} was called.
* - miss: the item is not in cache, and will be fetched.
* - hit: the item is in the cache, and was resolved immediately.
* - stale: the item is in the cache, but stale.
Expand Down Expand Up @@ -812,7 +813,9 @@ export namespace LRUCache {
* Changing any of these will alter the defaults for subsequent method calls,
* but is otherwise safe.
*/
export class LRUCache<K extends {}, V extends {}, FC = unknown> implements Map<K,V> {
export class LRUCache<K extends {}, V extends {}, FC = unknown>
implements Map<K, V>
{
// properties coming in from the options of these, only max and maxSize
// really *need* to be protected. The rest can be modified, as they just
// set defaults for various methods.
Expand Down Expand Up @@ -2180,6 +2183,56 @@ export class LRUCache<K extends {}, V extends {}, FC = unknown> implements Map<K
}
}

/**
* In some cases, `cache.fetch()` may resolve to `undefined`, either because
* a {@link LRUCache.OptionsBase#fetchMethod} was not provided (turning
* `cache.fetch(k)` into just an async wrapper around `cache.get(k)`) or
* because `ignoreFetchAbort` was specified (either to the constructor or
* in the {@link LRUCache.FetchOptions}). Also, the
* {@link OptionsBase.fetchMethod} may return `undefined` or `void`, making
* the test even more complicated.
*
* Because inferring the cases where `undefined` might be returned are so
* cumbersome, but testing for `undefined` can also be annoying, this method
* can be used, which will reject if `this.fetch()` resolves to undefined.
*/
forceFetch(
k: K,
fetchOptions: unknown extends FC
? LRUCache.FetchOptions<K, V, FC>
: FC extends undefined | void
? LRUCache.FetchOptionsNoContext<K, V>
: LRUCache.FetchOptionsWithContext<K, V, FC>
): Promise<V>
// this overload not allowed if context is required
forceFetch(
k: unknown extends FC
? K
: FC extends undefined | void
? K
: never,
fetchOptions?: unknown extends FC
? LRUCache.FetchOptions<K, V, FC>
: FC extends undefined | void
? LRUCache.FetchOptionsNoContext<K, V>
: never
): Promise<V>
async forceFetch(
k: K,
fetchOptions: LRUCache.FetchOptions<K, V, FC> = {}
): Promise<V> {
const v = await this.fetch(
k,
fetchOptions as unknown extends FC
? LRUCache.FetchOptions<K, V, FC>
: FC extends undefined | void
? LRUCache.FetchOptionsNoContext<K, V>
: LRUCache.FetchOptionsWithContext<K, V, FC>
)
if (v === undefined) throw new Error('fetch() returned undefined')
return v
}

/**
* Return a value from the cache. Will update the recency of the cache
* entry found.
Expand Down
5 changes: 4 additions & 1 deletion test/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ t.test('abort, but then keep on fetching anyway', async t => {
t.equal(ac.signal.reason, er)
t.equal(cache.get(1), 1)

const p2 = cache.fetch(2)
const p2 = cache.forceFetch(2)
t.equal(cache.get(2), undefined)
cache.delete(2)
t.equal(cache.get(2), undefined)
Expand All @@ -696,6 +696,9 @@ t.test('abort, but then keep on fetching anyway', async t => {
const p4 = cache.fetch(4)
clock.advance(100)
t.equal(await p4, undefined)
const p5 = cache.forceFetch(4)
clock.advance(100)
await t.rejects(p5, { message: 'fetch() returned undefined' })
t.same(e.valList, before, 'did not update values with undefined')
})

Expand Down

0 comments on commit 01b4c0c

Please sign in to comment.