lru-cache
Advanced tools
Comparing version 7.16.2 to 7.17.0
@@ -524,2 +524,4 @@ // Project: https://github.com/isaacs/node-lru-cache | ||
* | ||
* This may be overridden in the {@link fetchMethod}. | ||
* | ||
* @default false | ||
@@ -537,2 +539,4 @@ * @since 7.10.0 | ||
* | ||
* This may be overridden in the {@link fetchMethod}. | ||
* | ||
* @default false | ||
@@ -544,2 +548,48 @@ * @since 7.16.0 | ||
/** | ||
* | ||
* Set to true to ignore the `abort` event emitted by the `AbortSignal` | ||
* object passed to {@link fetchMethod}, and still cache the | ||
* resulting resolution value, as long as it is not `undefined`. | ||
* | ||
* When used on its own, this means aborted {@link fetch} calls are not | ||
* immediately resolved or rejected when they are aborted, and instead take | ||
* the full time to await. | ||
* | ||
* When used with {@link allowStaleOnFetchAbort}, aborted {@link fetch} | ||
* calls will resolve immediately to their stale cached value or | ||
* `undefined`, and will continue to process and eventually update the | ||
* cache when they resolve, as long as the resulting value is not | ||
* `undefined`, thus supporting a "return stale on timeout while | ||
* refreshing" mechanism by passing `AbortSignal.timeout(n)` as the signal. | ||
* | ||
* **Note**: regardless of this setting, an `abort` event _is still emitted | ||
* on the `AbortSignal` object_, so may result in invalid results when | ||
* passed to other underlying APIs that use AbortSignals. | ||
* | ||
* This may be overridden in the {@link fetchMethod} or the call to | ||
* {@link fetch}. | ||
* | ||
* @default false | ||
* @since 7.17.0 | ||
*/ | ||
ignoreFetchAbort?: boolean | ||
/** | ||
* Set to true to return a stale value from the cache when the | ||
* `AbortSignal` passed to the {@link fetchMethod} dispatches an `'abort'` | ||
* event, whether user-triggered, or due to internal cache behavior. | ||
* | ||
* Unless {@link ignoreFetchAbort} is also set, the underlying | ||
* {@link fetchMethod} will still be considered canceled, and its return | ||
* value will be ignored and not cached. | ||
* | ||
* This may be overridden in the {@link fetchMethod} or the call to | ||
* {@link fetch}. | ||
* | ||
* @default false | ||
* @since 7.17.0 | ||
*/ | ||
allowStaleOnFetchAbort?: boolean | ||
/** | ||
* Set to any value in the constructor or {@link fetch} options to | ||
@@ -624,4 +674,4 @@ * pass arbitrary data to the {@link fetchMethod} in the {@link context} | ||
* resulting {@link set} operation on resolution, or in the case of | ||
* {@link noDeleteOnFetchRejection} and {@link allowStaleOnFetchRejection}, | ||
* the handling of failure. | ||
* {@link noDeleteOnFetchRejection}, {@link ignoreFetchAbort}, and | ||
* {@link allowStaleOnFetchRejection}, the handling of failure. | ||
*/ | ||
@@ -639,2 +689,4 @@ interface FetcherFetchOptions<K, V> { | ||
allowStaleOnFetchRejection?: boolean | ||
ignoreFetchAbort?: boolean | ||
allowStaleOnFetchAbort?: boolean | ||
} | ||
@@ -653,2 +705,3 @@ | ||
fetchContext?: any | ||
signal?: AbortSignal | ||
} | ||
@@ -655,0 +708,0 @@ |
68
index.js
@@ -171,2 +171,4 @@ const perf = | ||
allowStaleOnFetchRejection, | ||
allowStaleOnFetchAbort, | ||
ignoreFetchAbort, | ||
} = options | ||
@@ -242,2 +244,4 @@ | ||
this.allowStaleOnFetchRejection = !!allowStaleOnFetchRejection | ||
this.allowStaleOnFetchAbort = !!allowStaleOnFetchAbort | ||
this.ignoreFetchAbort = !!ignoreFetchAbort | ||
@@ -747,2 +751,7 @@ // NB: maxEntrySize is set to maxSize if it's set | ||
const ac = new AC() | ||
if (options.signal) { | ||
options.signal.addEventListener('abort', () => | ||
ac.abort(options.signal.reason) | ||
) | ||
} | ||
const fetchOpts = { | ||
@@ -753,27 +762,44 @@ signal: ac.signal, | ||
} | ||
const cb = v => { | ||
if (!ac.signal.aborted) { | ||
this.set(k, v, fetchOpts.options) | ||
return v | ||
} else { | ||
const cb = (v, updateCache = false) => { | ||
const { aborted } = ac.signal | ||
const ignoreAbort = options.ignoreFetchAbort && v !== undefined | ||
if (aborted && !ignoreAbort && !updateCache) { | ||
return eb(ac.signal.reason) | ||
} | ||
// either we didn't abort, and are still here, or we did, and ignored | ||
if (this.valList[index] === p) { | ||
if (v === undefined) { | ||
if (p.__staleWhileFetching) { | ||
this.valList[index] = p.__staleWhileFetching | ||
} else { | ||
this.delete(k) | ||
} | ||
} else { | ||
this.set(k, v, fetchOpts.options) | ||
} | ||
} | ||
return v | ||
} | ||
const eb = er => { | ||
const { aborted } = ac.signal | ||
const allowStaleAborted = | ||
aborted && options.allowStaleOnFetchAbort | ||
const allowStale = | ||
allowStaleAborted || options.allowStaleOnFetchRejection | ||
const noDelete = allowStale || options.noDeleteOnFetchRejection | ||
if (this.valList[index] === p) { | ||
// if we allow stale on fetch rejections, then we need to ensure that | ||
// the stale value is not removed from the cache when the fetch fails. | ||
const noDelete = | ||
options.noDeleteOnFetchRejection || | ||
options.allowStaleOnFetchRejection | ||
const del = !noDelete || p.__staleWhileFetching === undefined | ||
if (del) { | ||
this.delete(k) | ||
} else { | ||
} else if (!allowStaleAborted) { | ||
// still replace the *promise* with the stale value, | ||
// since we are done with the promise at this point. | ||
// leave it untouched if we're still waiting for an | ||
// aborted background fetch that hasn't yet returned. | ||
this.valList[index] = p.__staleWhileFetching | ||
} | ||
} | ||
if (options.allowStaleOnFetchRejection) { | ||
if (allowStale) { | ||
return p.__staleWhileFetching | ||
@@ -785,4 +811,18 @@ } else if (p.__returned === p) { | ||
const pcall = (res, rej) => { | ||
ac.signal.addEventListener('abort', () => res()) | ||
this.fetchMethod(k, v, fetchOpts).then(res, rej) | ||
this.fetchMethod(k, v, fetchOpts).then(v => res(v), rej) | ||
// ignored, we go until we finish, regardless. | ||
// defer check until we are actually aborting, | ||
// so fetchMethod can override. | ||
ac.signal.addEventListener('abort', () => { | ||
if ( | ||
!options.ignoreFetchAbort || | ||
options.allowStaleOnFetchAbort | ||
) { | ||
res() | ||
// when it eventually resolves, update the cache. | ||
if (options.allowStaleOnFetchAbort) { | ||
res = v => cb(v, true) | ||
} | ||
} | ||
}) | ||
} | ||
@@ -833,2 +873,4 @@ const p = new Promise(pcall).then(cb, eb) | ||
allowStaleOnFetchRejection = this.allowStaleOnFetchRejection, | ||
ignoreFetchAbort = this.ignoreFetchAbort, | ||
allowStaleOnFetchAbort = this.allowStaleOnFetchAbort, | ||
fetchContext = this.fetchContext, | ||
@@ -858,2 +900,4 @@ forceRefresh = false, | ||
allowStaleOnFetchRejection, | ||
allowStaleOnFetchAbort, | ||
ignoreFetchAbort, | ||
signal, | ||
@@ -860,0 +904,0 @@ } |
{ | ||
"name": "lru-cache", | ||
"description": "A cache object that deletes the least-recently-used items.", | ||
"version": "7.16.2", | ||
"version": "7.17.0", | ||
"author": "Isaac Z. Schlueter <i@izs.me>", | ||
@@ -6,0 +6,0 @@ "keywords": [ |
@@ -248,2 +248,58 @@ # lru-cache | ||
### `allowStaleOnFetchAbort` | ||
Set to true to return a stale value from the cache when the | ||
`AbortSignal` passed to the `fetchMethod` dispatches an `'abort'` | ||
event, whether user-triggered, or due to internal cache behavior. | ||
Unless `ignoreFetchAbort` is also set, the underlying | ||
`fetchMethod` will still be considered canceled, and its return | ||
value will be ignored and not cached. | ||
### `ignoreFetchAbort` | ||
Set to true to ignore the `abort` event emitted by the | ||
`AbortSignal` object passed to `fetchMethod`, and still cache the | ||
resulting resolution value, as long as it is not `undefined`. | ||
When used on its own, this means aborted `fetch()` calls are not | ||
immediately resolved or rejected when they are aborted, and | ||
instead take the full time to await. | ||
When used with `allowStaleOnFetchAbort`, aborted `fetch()` calls | ||
will resolve immediately to their stale cached value or | ||
`undefined`, and will continue to process and eventually update | ||
the cache when they resolve, as long as the resulting value is | ||
not `undefined`, thus supporting a "return stale on timeout while | ||
refreshing" mechanism by passing `AbortSignal.timeout(n)` as the | ||
signal. | ||
For example: | ||
```js | ||
const c = new LRUCache({ | ||
ttl: 100, | ||
ignoreFetchAbort: true, | ||
allowStaleOnFetchAbort: true, | ||
fetchMethod: async (key, oldValue, { signal }) => { | ||
// note: do NOT pass the signal to fetch()! | ||
// let's say this fetch can take a long time. | ||
const res = await fetch(`https://slow-backend-server/${key}`) | ||
return await res.json() | ||
}, | ||
}) | ||
// this will return the stale value after 100ms, while still | ||
// updating in the background for next time. | ||
const val = await c.fetch('key', { signal: AbortSignal.timeout(100) }) | ||
``` | ||
**Note**: regardless of this setting, an `abort` event _is still | ||
emitted on the `AbortSignal` object_, so may result in invalid | ||
results when passed to other underlying APIs that use | ||
AbortSignals. | ||
This may be overridden on the `fetch()` call or in the | ||
`fetchMethod` itself. | ||
### `dispose` | ||
@@ -512,4 +568,17 @@ | ||
### `async fetch(key, { updateAgeOnGet, allowStale, size, sizeCalculation, ttl, noDisposeOnSet, forceRefresh } = {}) => Promise` | ||
### `async fetch(key, options = {}) => Promise` | ||
The following options are supported: | ||
* `updateAgeOnGet` | ||
* `allowStale` | ||
* `size` | ||
* `sizeCalculation` | ||
* `ttl` | ||
* `noDisposeOnSet` | ||
* `forceRefresh` | ||
* `signal` - AbortSignal can be used to cancel the `fetch()` | ||
* `fetchContext` - sets the `context` option passed to the | ||
underlying `fetchMethod`. | ||
If the value is in the cache and not stale, then the returned | ||
@@ -519,4 +588,5 @@ Promise resolves to the value. | ||
If not in the cache, or beyond its TTL staleness, then | ||
`fetchMethod(key, staleValue, options)` is called, and the value | ||
returned will be added to the cache once resolved. | ||
`fetchMethod(key, staleValue, { options, signal, context })` is | ||
called, and the value returned will be added to the cache once | ||
resolved. | ||
@@ -545,2 +615,11 @@ If called with `allowStale`, and an asynchronous fetch is | ||
If the key is evicted or deleted before the `fetchMethod` | ||
resolves, then the AbortSignal passed to the `fetchMethod` will | ||
receive an `abort` event, and the promise returned by `fetch()` | ||
will reject with the reason for the abort. | ||
If a `signal` is passed to the `fetch()` call, then aborting the | ||
signal will abort the fetch and cause the `fetch()` promise to | ||
reject with the reason provided. | ||
### `peek(key, { allowStale } = {}) => value` | ||
@@ -547,0 +626,0 @@ |
Sorry, the diff of this file is not supported yet
117037
2702
958
133
1
1
1
8