Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | 2x 2x 2x 2x 2x 4x 4x 2x 2x 2x 2x 2x 2x 2x 2x 2x | import { RejectFn, ResolveFn } from "./_types";
/**
* Create a {@link Promise}-returning {@link Function} that returns the
* memoized results of a {@link Promise}. The key for the lookup cache is
* determined by the `keyExtractor` {@link Function}.
*
* An optional {@link Map} instance can be provided to be used as cache. This
* allows for example to prepopulate the cache, or to force recomputation of
* some values by removing their corresponding keys. If no instance is provided
* an empty {@link Map} will be created.
*
* @remarks
* If the {@link Promise} throws an error, no result will be cached and the
* {@link Promise} will be run again next time.
*
* @example
* Memoize the `fetch` function
* ```ts
* const keyExtractor = (url) => url;
*
* const memoizedFetch = memoizePromise(fetch, keyExtractor);
*
* const result1 = memoizedFetch("https://...");
* const result2 = memoizedFetch("https://...");
* // fetch will only be called once
* ```
*
* @example
* Provide an initial cache restored from database
* ```ts
* const restoredCacheFromDatabase = new Map([["https://...", 1]]);
*
* const keyExtractor = (url) => url;
*
* const memoizedFetch = memoizePromise(fetch, keyExtractor, restoredCacheFromDatabase);
*
* const result = memoizedFetch("https://...");
* // result will be directly returned from cache
* ```
*
* @example
* Clear cache after 5 seconds
* ```ts
* const cache = new Map();
* const keyExtractor = (url) => url;
*
* const memoizedFetch = memoizePromise(fetch, keyExtractor, cache);
*
* // ... fill the cache
*
* setTimeout(() => {
* cache.clear();
* }, 5000);
* ```
*
* @param fn - the {@link Function} to memoize
* @param keyExtractor - the {@link Function} to extract the key for the cache
* @param cache - an optional, possibly prefilled {@link Map} instance to use as cache
* @returns the memoized result
*/
export const memoizePromise = <TArgs extends unknown[], R>(
fn: (...args: TArgs) => PromiseLike<R>,
keyExtractor: (...args: TArgs) => unknown,
cache = new Map<unknown, R>()
): ((...args: TArgs) => Promise<R>) => {
const pending = new Set<unknown>();
const resolves: ResolveFn<R>[] = [];
const rejects: RejectFn[] = [];
return async (...args) => {
const key = keyExtractor(...args);
if (cache.has(key)) {
return cache.get(key) as R;
}
Iif (pending.has(key)) {
return new Promise((resolve, reject) => {
resolves.push(resolve);
rejects.push(reject);
});
}
try {
pending.add(key);
const value = await fn(...args);
pending.delete(key);
cache.set(key, value);
resolves.forEach((resolve) => resolve(value));
return value;
} catch (err) {
pending.delete(key);
rejects.forEach((reject) => reject(err));
throw err;
}
};
};
|