Last active
December 3, 2022 15:08
-
-
Save fuzzypawzz/b8ab885e502abee7a87d5b5a05a82a71 to your computer and use it in GitHub Desktop.
TypeScript Promise cache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { v4 as uuidv4 } from 'uuid' | |
type CachedPromise = Promise<any> | |
type PromiseDetails = Record<string, unknown> | |
type PromiseId = string | |
type MatcherCallback = (args: PromiseDetails) => boolean | |
type ActionId = string | |
interface IPromiseDataModel { | |
id: PromiseId | |
promise: CachedPromise | |
details: PromiseDetails | |
} | |
type PromiseStorage = Record<string, IPromiseDataModel[]> | |
interface IRequestDetails { | |
actionId: ActionId | |
action: () => CachedPromise | |
requestDetails: PromiseDetails | |
matcher: MatcherCallback | |
} | |
type CreateCachedRequest = <PromiseReturnType>( | |
args: IRequestDetails | |
) => Promise<PromiseReturnType> | |
enum ERROR_MESSAGE { | |
CALLBACK_REQUIRED = 'Matcher callback is required.', | |
DATA_MODEL_MISSING = 'Cached promise model does not exist for actionId:', | |
} | |
/** | |
* Only allowed actions can create a cache. | |
* These are unique action identifiers here. | |
*/ | |
const actionIdentifiers = [ | |
'SOME_ACTION_ID' | |
] | |
const createPromiseStorage = (): PromiseStorage => | |
actionIdentifiers.reduce( | |
(result, actionId) => Object.assign(result, { [actionId]: [] }), | |
{} | |
) | |
/** | |
* PROMISE CACHING HELPER | |
* | |
* @method promiseCache Closure | |
* | |
* @description Use this helper to prevent calling an async method several times while there is an active | |
* unfulfilled promise. You can use it to prevent spamming a specific endpoint if multiple | |
* components are calling a method to fetch some data. | |
* The unfulfilled promise will be returned instead of making a new call to the method. | |
* The cached promise will be removed from the cache as soon as it's resolved or rejected. | |
* | |
* @returns A method that when called creates a cache for the promise and returns the cached promise. | |
*/ | |
export const promiseCache = ((): CreateCachedRequest => { | |
const storage = createPromiseStorage() | |
const removePromise = (model: IPromiseDataModel[], id: PromiseId): void => { | |
const index = model.findIndex((model) => model.id === id) | |
if (index !== -1) model.splice(index, 1) | |
} | |
return ({ actionId, requestDetails, matcher, action }) => { | |
if (!matcher) throw new Error(ERROR_MESSAGE.CALLBACK_REQUIRED) | |
const promiseDataModel = storage[actionId] | |
if (!promiseDataModel) | |
throw new Error(`${ERROR_MESSAGE.DATA_MODEL_MISSING} ${actionId}`) | |
const existingRequest = promiseDataModel.find(({ details }) => | |
matcher(details) | |
) | |
if (existingRequest) return existingRequest.promise | |
const id = uuidv4() | |
const request: IPromiseDataModel = { | |
id, | |
details: requestDetails, | |
promise: action().finally(() => removePromise(promiseDataModel, id)), | |
} | |
promiseDataModel.push(request) | |
return request.promise | |
} | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment