{"id":21754603,"url":"https://github.com/mdawar/memoize-utils","last_synced_at":"2026-02-27T20:41:46.476Z","repository":{"id":40436462,"uuid":"506952416","full_name":"mdawar/memoize-utils","owner":"mdawar","description":"Memoize function and TypeScript decorator for sync and async functions.","archived":false,"fork":false,"pushed_at":"2022-07-03T13:08:18.000Z","size":149,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-05T20:28:22.929Z","etag":null,"topics":["cache","caching","javascript","memoize","memoize-async","memoize-decorator","promise","promise-cache","promise-memoization","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mdawar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-24T09:28:43.000Z","updated_at":"2023-03-07T15:29:05.000Z","dependencies_parsed_at":"2022-08-25T22:50:17.094Z","dependency_job_id":null,"html_url":"https://github.com/mdawar/memoize-utils","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/mdawar/memoize-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdawar%2Fmemoize-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdawar%2Fmemoize-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdawar%2Fmemoize-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdawar%2Fmemoize-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdawar","download_url":"https://codeload.github.com/mdawar/memoize-utils/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdawar%2Fmemoize-utils/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29912606,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T19:37:42.220Z","status":"ssl_error","status_checked_at":"2026-02-27T19:37:41.463Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","caching","javascript","memoize","memoize-async","memoize-decorator","promise","promise-cache","promise-memoization","typescript"],"created_at":"2024-11-26T09:14:28.637Z","updated_at":"2026-02-27T20:41:46.460Z","avatar_url":"https://github.com/mdawar.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# memoize-utils\n\n[![npm](https://img.shields.io/npm/v/memoize-utils)](https://www.npmjs.com/package/memoize-utils)\n\n[Memoize](https://en.wikipedia.org/wiki/Memoization) sync and async functions (Returning a `Promise`).\n\nCache expensive function calls and return the cached result when the same inputs occur again.\n\nThis package provides:\n\n- `memoize` Function: Used to memoize any sync or `async` function.\n- `memoize` Decorator: **TypeScript** decorator used to memoize class methods and getters.\n\nCan be used to:\n\n- Cache expensive function calls\n- Prevent hitting rate limits on an API when the result can be cached\n- Speed up programs and prevent unnecessary computations and bandwidth usage\n\n## Installation\n\n#### Node.js + Browsers\n\n```\n$ npm i memoize-utils\n```\n\n#### Deno\n\n```ts\n// Memoize function\nimport { memoize } from 'https://unpkg.com/memoize-utils@1.0.1/dist/esm/index.js';\n\n// Memoize decorator\nimport { memoize } from 'https://unpkg.com/memoize-utils@1.0.1/dist/esm/decorator.js';\n\n// Cache ID helpers\nimport { all, json, anyOrder } from 'https://unpkg.com/memoize-utils@1.0.1/dist/esm/helpers.js';\n```\n\n## Usage\n\nMemoizing a function:\n\n```js\nimport { memoize } from 'memoize-utils';\n\n// Works with sync and async functions\nfunction expensiveFunction() {\n  // Some expensive operation that we want to cache its result\n  return result;\n}\n\nconst memoized = memoize(expensiveFunction);\n\nmemoized();\nmemoized(); // Returns cached result\n```\n\nExample with cache expiration:\n\n```js\nimport { memoize } from 'memoize-utils';\n\nasync function fetchIP() {\n  const response = await fetch('http://httpbin.org/ip');\n  return response.json();\n}\n\nconst memoized = memoize(fetchIP, { maxAge: 2000 }); // Expires after 2 seconds\n\n// The first request is cached\nawait memoized();\n\n// Subsequent calls return the cached result\nawait memoized();\n\n// Delay 2 seconds\nawait new Promise((resolve) =\u003e setTimeout(resolve, 2000));\n\n// Cache has expired, make a new request and cache the result\nawait memoized();\n```\n\nMemoizing class methods and getters:\n\n```js\nimport { memoize } from 'memoize-utils/decorator';\n\nclass ExampleClass {\n  @memoize({ maxAge: 2000 })\n  async fetchIP() {\n    const response = await fetch('http://httpbin.org/ip');\n    return response.json();\n  }\n\n  @memoize({ maxAge: 60 * 60 * 1000 })\n  get result() {\n    // ...\n  }\n}\n\nconst instance = new ExampleClass();\n\n// First call is cached and subsequent calls return the result from the cache until expiration\nawait instance.fetchIP();\nawait instance.fetchIP(); // Cached\n\n// First access is cached, any access later returns the cached result until expiration\ninstance.result;\n```\n\n## Options\n\n| Option                 | Description                                                                                                       | Default     |\n| ---------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------- |\n| `maxAge`               | Cached results expiration duration in milliseconds (Defaults to no expiration).                                   | `undefined` |\n| `cache`                | Custom cache instance or a factory function returning a cache instance.                                           | `new Map()` |\n| `cacheId`              | Custom cache ID function, to be used to determine the ID of the cached result (Defaults to first argument as ID). | `undefined` |\n| `cacheRejectedPromise` | Cache the rejected promise when memoizing an `async` function.                                                    | `false`     |\n| `cacheFromContext`     | Function returning a custom cache instance that has access to the original function's context `this`.             | `undefined` |\n\nTo customize these defaults, you can create a wrapper function:\n\n```js\nimport { memoize as memoizeFn } from 'memoize-utils';\n\nexport function memoize(fn, options) {\n  const defaults = {\n    maxAge: 60 * 60 * 1000, // Cache expires in 1 hour\n    cache: new LRUCache(), // Use a custom cache instance\n    cacheId: (obj) =\u003e obj.id, // Use a specific ID assuming your first arg is an object\n    // ...\n  };\n\n  return memoizeFn(fn, { defaults, ...options });\n}\n\n// Use the new wrapper function\nconst memoized = memoize(expensiveFunction);\n```\n\n## Cache Expiration\n\nCached results are stored with a timestamp, the `maxAge` option can be passed when creating the memoized function to set the expiration duration of the cache.\nThe cache expiration is checked when the cache is accessed, so there are no timers that clear the cache automatically, if you need this functionality you can pass a custom `cache` object that supports it.\n\n```js\nconst memoized = memoize(expensiveFunction, { maxAge: 60 * 60 * 1000 }); // Cache results for 1 hour\n```\n\n## Custom Cache\n\nBy default a [`Map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object is used to store the cached results, any object that implements a similar API can be used instead, for example [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap).\n\n```js\nconst cache = new WeakMap();\n\nconst memoized = memoize(expensiveFunction, { cache });\n\n// We can also pass a factory function that returns a cache instance\nconst memoized = memoize(expensiveFunction, {\n  cache: () =\u003e new WeakMap(),\n});\n```\n\nRequired `cache` object methods:\n\n- `.set(key, value)`\n- `.get(key)`\n- `.has(key)`\n- `.delete(key)`\n\n## Cache ID\n\nBy default the **first argument** of the memoized function is used as the cache ID to store the result.\n\n```js\nconst memoized = memoize(expensiveFunction);\n\n// All of these 3 calls are considered the same since we're using the first argument as the cache ID\nmemoized('a');\nmemoized('a', 'b'); // Cached\nmemoized('a', 'b', 1); // Cached\n```\n\nTo use all the arguments as the cache ID, we can pass a `cacheId` function:\n\n```js\nfunction expensiveFunction(a, b, c) {\n  // ...\n}\n\nconst memoized = memoize(expensiveFunction, {\n  // The cacheId function accepts the same arguments as the original function\n  cacheId: (...args) =\u003e args.map(String).join('-'),\n});\n\n// In this case, each of these calls is cached separately\nmemoized('a');\nmemoized('a', 'b');\nmemoized('a', 'b', 1);\n```\n\nObject arguments require serialization, for example using `JSON.stringify` or any other serialization method.\n\n```js\nfunction expensiveFunction(a = {}, b = null, c = true) {\n  // ...\n}\n\nconst memoized = memoize(expensiveFunction, {\n  // Assuming all the arguments are JSON serializable\n  cacheId: (...args) =\u003e JSON.stringify(args),\n});\n\n// Without serialization these calls wouldn't be considered the same\nmemoized({});\nmemoized({}); // Cached\nmemoized({}); // Cached\n```\n\n`RegExp` as arguments also must be serialized, for simplicity we can use `RegExp.toString()`.\n\n```js\nfunction expensiveFunction(a) {\n  // ...\n}\n\nconst memoized = memoize(expensiveFunction, {\n  cacheId: (a) =\u003e a.toString(),\n});\n\n// Without serialization these calls wouldn't be considered the same\nmemoized(/(.*)/);\nmemoized(/(.*)/); // Cached\nmemoized(/(.*)/); // Cached\n```\n\n### Cache ID Helpers\n\nThe module `memoize-utils/helpers` provides some commonly used `cacheId` functions:\n\n- `all`: Get an ID from all the arguments casted to a string and then joined together.\n- `json`: Get a JSON string ID from the arguments (`JSON.stringify(args)`).\n- `anyOrder`: Get the same ID from a set of arguments passed in any order.\n\nUsage:\n\n```js\nimport { all, json, anyOrder } from 'memoize-utils/helpers';\n\n// Use all the arguments as an ID\n// Note: does not work with objects (Arguments are casted to strings)\n// but it works with `RegExp` objects\nmemoize(fn, { cacheId: all });\n\n// Use all the arguments as an ID including objects\n// Note: does not work with `RegExp` objects\nmemoize(fn, { cacheId: json });\n\n// Use all the arguments as an ID but in any order\n// Note: does not work with objects (Arguments are casted to strings)\nmemoize(fn, { cacheId: anyOrder });\n```\n\nYou can create your own `memoize` wrapper function using a custom cache ID:\n\n```js\nimport { memoize } from 'memoize-utils';\nimport { anyOrder } from 'memoize-utils/helpers';\n\nexport function memoizeAnyOrder(fn, options) {\n  return memoize(fn, { cacheId: anyOrder, ...options });\n}\n\n// Memoize functions using all of the arguments as a cache ID in any order\nconst memoized = memoizeAnyOrder(fn);\n```\n\n## Rejected Promises\n\nBy default rejected promises are not cached, this is done to have the same functionality for synchronous functions when throwing errors.\nIf you want to also cache rejected promises, you can use the `cacheRejectedPromise` option.\n\n```js\n// Cache rejected promises\n// You might want to use it with `maxAge` so the result expires at some point and the original function call again\nconst memoized = memoize(expensiveFunction, { cacheRejectedPromise: true });\n```\n\n## Cache From Context\n\nIf your cache instance requires the original function's context (`this`), you can use `cacheFromContext` function that has access to the same context as the original function and return a cache instance.\nFor example this function is used to implement the decorator which uses a separate cache for each class instance.\n\n```js\nfunction cacheFromContext() {\n  // You have acces to the original function's context\n  if (!this.cache) {\n    this.cache = new Map();\n  }\n\n  return this.cache;\n}\n\nconst memoized = memoize(expensiveFunction, { cacheFromContext });\n```\n\nExample using a `cache` instance property:\n\n```ts\nclass ExampleClass {\n  cache: Map\u003cany, any\u003e;\n\n  constructor(public index: number) {\n    this.cache = new Map();\n  }\n\n  @memoize({\n    cacheFromContext(this: any) {\n      return this.cache;\n    },\n  })\n  count() {\n    return this.index++;\n  }\n}\n\nconst instance = new ExampleClass(0);\n\n// The memoized method will be using the instance's `cache` property to store the results\ninstance.count(); // 0\ninstance.cache.size; // 1\ninstance.cache.clear();\n```\n\n## Decorator Support\n\nThe [`moduleResolution`](https://www.typescriptlang.org/tsconfig#moduleResolution) config option must be set to `node16` to be able to import the decorator.\n\n```ts\nimport { memoize } from 'memoize-utils/decorator';\n```\n\nTo enable support for [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html) in your TypeScript project:\n\n- The `experimentalDecorators` TypeScript config must be set to `true` in the `tsconfig.json` file:\n\n```json\n{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true\n  }\n}\n```\n\n- Or using the command line option `tsc --experimentalDecorators`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdawar%2Fmemoize-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdawar%2Fmemoize-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdawar%2Fmemoize-utils/lists"}