{"id":19362690,"url":"https://github.com/svandriel/cachify-promise","last_synced_at":"2025-04-23T12:33:06.150Z","repository":{"id":38427102,"uuid":"243515383","full_name":"svandriel/cachify-promise","owner":"svandriel","description":"Smart caching for promises. Like memoization, but better.","archived":false,"fork":false,"pushed_at":"2024-12-24T16:09:18.000Z","size":571,"stargazers_count":8,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-28T22:16:59.359Z","etag":null,"topics":["async","cache","deduplicate","memoization","promise"],"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/svandriel.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-02-27T12:35:31.000Z","updated_at":"2024-12-24T16:09:16.000Z","dependencies_parsed_at":"2024-11-10T07:30:59.387Z","dependency_job_id":"14cbd862-9e71-4092-bc71-000782d6e867","html_url":"https://github.com/svandriel/cachify-promise","commit_stats":{"total_commits":71,"total_committers":2,"mean_commits":35.5,"dds":"0.15492957746478875","last_synced_commit":"64d2a3068e8a7f3f757be19fb8f9335f16951b65"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svandriel%2Fcachify-promise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svandriel%2Fcachify-promise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svandriel%2Fcachify-promise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svandriel%2Fcachify-promise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/svandriel","download_url":"https://codeload.github.com/svandriel/cachify-promise/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250435238,"owners_count":21430242,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["async","cache","deduplicate","memoization","promise"],"created_at":"2024-11-10T07:29:55.631Z","updated_at":"2025-04-23T12:33:01.137Z","avatar_url":"https://github.com/svandriel.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cachify-promise\n\n[![Build status](https://travis-ci.com/svandriel/cachify-promise.svg?branch=master)](https://travis-ci.com/svandriel/cachify-promise)\n\nSmart caching for promises. Like memoization, but better.\n\n```javascript\nconst { cachifyPromise } = require('cachify-promise');\n\nconst cachedFetch = cachifyPromise(fetch);\n\nawait Promise.all([\n    cachedFetch('/api/users/1'),\n    cachedFetch('/api/users/1'),\n    cachedFetch('/api/users/1'),\n    cachedFetch('/api/users/42')\n]); // Results in only 2 calls\n\nconst user = await cachedFetch('/api/users/42'); // From cache\n```\n\n## Installation\n\n```\nnpm install cachify-promise\n```\n\n## Features\n\n-   Promise deduplication\n-   Caches resolved values\n-   Ignores failed promises (unlike most memoization functions)\n-   Deletion of items\n-   Fully Typescript-ready\n-   Supports stale-while-revalidate caching policy\n-   Cleans expired items from the cache periodically\n-   Customizable (time-to-live, custom cache storage, key generation)\n-   No dependencies\n-   Works in browsers (requires `Promise` and `Map` to be available or polyfilled)\n-   Works in Node.js (10 or above)\n\n## Usage\n\n```javascript\nconst cachedFn = cachifyPromise(fn, options);\n```\n\n-   `fn`: A function returning a promise\n-   `options`\n    -   `ttl`: Time-to-live in _milliseconds_ (defaults to `Number.MAX_VALUE`). Set to `0` to disable caching of resolved values.\n    -   `staleWhileRevalidate`: Enable 'stale-while-revalidate' policy (defaults to `false`)\n    -   `cacheMap`: Cache instance, must implement `has`, `get`, `set`, `delete`. Defaults to `new Map()`.\n    -   `cacheKeyFn`: Function for generating cache keys, must return strings.\n    -   `cleanupInterval`: Time in _milliseconds_ that determines the interval at which a cleanup job is run. This job clears any expired cache items. Defaults to 10000 ms.\n    -   `statsFn`: Callback function to receive stats. Will be called on each update with an object containing `hitPromise`, `hitValue`, `miss` and `put` values.\n-   Returns a function with the same signature as `fn`.\n\n### Full example\n\n```javascript\nconst { cachifyPromise } = require('cachify-promise');\n\nconst cachedFetch = cachifyPromise(fetch, {\n    ttl: 3600 * 1000, // one hour\n    cacheKeyFn: (url, options) =\u003e `${options.method} ${url}`,\n    cacheMap: new Map(),\n    staleWhileRevalidate: true,\n    statsFn: stats =\u003e console.log('Cache statistics:', stats)\n});\n```\n\n## Deduplication\n\nWhen performing expensive or time-consuming asynchronous tasks, it is often desirable to deduplicate calls while they are being done.\n\nImagine the `fetchUser` function will make a HTTP call to fetch information about a user.\n\nNaively, the following code will result in 2 HTTP calls being made:\n\n```javascript\n// In UI component 1\nconst userPromise1 = fetchUser({ id: 1 });\n// --\u003e triggers HTTP call\n\n// At the same time, in UI component 2\nconst userPromise2 = fetchUser({ id: 1 });\n// --\u003e triggers HTTP call\n```\n\nBy wrapping the `fetchUser` function with `cachifyPromise`, only a single call will be made at the same time:\n\n```javascript\nconst cachedFetchUser = cachifyPromise(fetchUser);\n\n// In UI component 1\nconst userPromise1 = cachedFetchUser({ id: 1 });\n// --\u003e triggers HTTP call\n\n// At the same time, in UI component 2\nconst userPromise2 = cachedFetchUser({ id: 1 });\n// --\u003e returns previous promise\n```\n\n## Response caching\n\nBy default, resolved values will be cached for for a long time (`Number.MAX_VALUE` milliseconds).\n\nWhen a promise rejects, this will _not_ be stored.\n\nYou can customize the time-to-live using the `ttl` option (see Usage).\nTo disable caching of resolved values altogether, set `ttl` to `0`.\n\nThe cache key is determined by running `JSON.stringify` over the argument array passed to the function. You can provide your own key-generating function with the `cacheKeyFn` option (see Usage).\n\n## Cleanup\n\nWhen there are items in the cache, a periodic cleanup job is run to clean any expired items in the cache. The interval at which this job is run may be controlled with the `cleanupInterval` option.\n\n**NOTE**: cleanup is not run when the `staleWhileRevalidate` policy is active\n\n## Deleting items from the cache\n\nYou can delete entries from the cache by invoking the `.delete()` function. This function takes the same arguments as a regular invocation.\n\n```javascript\nconst cachedFetchUser = cachifyPromise(fetchUser);\n\n// Invoke and store in cache\nawait cachedFetchedUser({ id: 1 });\n\n// Removes user 1 from the cache\ncachedFetchedUser.delete({ id: 1 });\n```\n\n## Stale while revalidate\n\nSometimes, it is acceptable to return a stale ('old') value when a cache item is past its time-to-live. In the meantime, a fresh value is being fetched in the background.\n\n```javascript\nconst cachedFetchUser = cachifyPromise(fetchUser, {\n    staleWhileRevalidate: true,\n    ttl: 10000\n});\n\n// In UI component\nconst userPromise1 = cachedFetchUser({ id: 1 });\n// --\u003e triggers HTTP call\n\n// \u003cHTTP call finishes\u003e\n\nawait delay(10001);\n\nconst userPromise2 = cachedFetchUser({ id: 1 });\n// --\u003e resolves with cached user, AND triggers HTTP call in the background\n\n// another 0.0001 seconds later\nconst userPromise3 = cachedFetchUser({ id: 1 });\n// --\u003e resolves with cached user, will NOT trigger HTTP call since one is already in progress\n\n// \u003cHTTP call finishes\u003e\n\nconst userPromise4 = cachedFetchUser({ id: 1 });\n// --\u003e new user data!\n```\n\n## Statistics\n\nWhen a `statsFn` function is provided (see Usage), that function will be invoked each time a cache interaction takes place. The object passed as a parameter to that function will contain:\n\n-   `hitPromise`: Cache hits on pending promises\n-   `hitValue`: Cache hits on stored values\n-   `miss`: Cache misses\n-   `put`: Cache puts\n\nThe number of cache accesses may be computed with:\n\n```javascript\naccess = stat.hitPromise + stat.hitValue + stat.miss;\n```\n\nAs such, the hit and miss ratios may be calculated with:\n\n```javascript\nhitRatio = (stat.hitPromise + stat.hitValue) / access;\nmissRatio = stat.miss / access;\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvandriel%2Fcachify-promise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsvandriel%2Fcachify-promise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvandriel%2Fcachify-promise/lists"}