npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

cachify-promise

v0.4.1

Published

Smart caching for promises. Like memoization, but better.

Downloads

795

Readme

cachify-promise

Build status

Smart caching for promises. Like memoization, but better.

const { cachifyPromise } = require('cachify-promise');

const cachedFetch = cachifyPromise(fetch);

await Promise.all([
    cachedFetch('/api/users/1'),
    cachedFetch('/api/users/1'),
    cachedFetch('/api/users/1'),
    cachedFetch('/api/users/42')
]); // Results in only 2 calls

const user = await cachedFetch('/api/users/42'); // From cache

Installation

npm install cachify-promise

Features

  • Promise deduplication
  • Caches resolved values
  • Ignores failed promises (unlike most memoization functions)
  • Deletion of items
  • Fully Typescript-ready
  • Supports stale-while-revalidate caching policy
  • Cleans expired items from the cache periodically
  • Customizable (time-to-live, custom cache storage, key generation)
  • No dependencies
  • Works in browsers (requires Promise and Map to be available or polyfilled)
  • Works in Node.js (10 or above)

Usage

const cachedFn = cachifyPromise(fn, options);
  • fn: A function returning a promise
  • options
    • ttl: Time-to-live in milliseconds (defaults to Number.MAX_VALUE). Set to 0 to disable caching of resolved values.
    • staleWhileRevalidate: Enable 'stale-while-revalidate' policy (defaults to false)
    • cacheMap: Cache instance, must implement has, get, set, delete. Defaults to new Map().
    • cacheKeyFn: Function for generating cache keys, must return strings.
    • 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.
    • statsFn: Callback function to receive stats. Will be called on each update with an object containing hitPromise, hitValue, miss and put values.
  • Returns a function with the same signature as fn.

Full example

const { cachifyPromise } = require('cachify-promise');

const cachedFetch = cachifyPromise(fetch, {
    ttl: 3600 * 1000, // one hour
    cacheKeyFn: (url, options) => `${options.method} ${url}`,
    cacheMap: new Map(),
    staleWhileRevalidate: true,
    statsFn: stats => console.log('Cache statistics:', stats)
});

Deduplication

When performing expensive or time-consuming asynchronous tasks, it is often desirable to deduplicate calls while they are being done.

Imagine the fetchUser function will make a HTTP call to fetch information about a user.

Naively, the following code will result in 2 HTTP calls being made:

// In UI component 1
const userPromise1 = fetchUser({ id: 1 });
// --> triggers HTTP call

// At the same time, in UI component 2
const userPromise2 = fetchUser({ id: 1 });
// --> triggers HTTP call

By wrapping the fetchUser function with cachifyPromise, only a single call will be made at the same time:

const cachedFetchUser = cachifyPromise(fetchUser);

// In UI component 1
const userPromise1 = cachedFetchUser({ id: 1 });
// --> triggers HTTP call

// At the same time, in UI component 2
const userPromise2 = cachedFetchUser({ id: 1 });
// --> returns previous promise

Response caching

By default, resolved values will be cached for for a long time (Number.MAX_VALUE milliseconds).

When a promise rejects, this will not be stored.

You can customize the time-to-live using the ttl option (see Usage). To disable caching of resolved values altogether, set ttl to 0.

The 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).

Cleanup

When 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.

NOTE: cleanup is not run when the staleWhileRevalidate policy is active

Deleting items from the cache

You can delete entries from the cache by invoking the .delete() function. This function takes the same arguments as a regular invocation.

const cachedFetchUser = cachifyPromise(fetchUser);

// Invoke and store in cache
await cachedFetchedUser({ id: 1 });

// Removes user 1 from the cache
cachedFetchedUser.delete({ id: 1 });

Stale while revalidate

Sometimes, 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.

const cachedFetchUser = cachifyPromise(fetchUser, {
    staleWhileRevalidate: true,
    ttl: 10000
});

// In UI component
const userPromise1 = cachedFetchUser({ id: 1 });
// --> triggers HTTP call

// <HTTP call finishes>

await delay(10001);

const userPromise2 = cachedFetchUser({ id: 1 });
// --> resolves with cached user, AND triggers HTTP call in the background

// another 0.0001 seconds later
const userPromise3 = cachedFetchUser({ id: 1 });
// --> resolves with cached user, will NOT trigger HTTP call since one is already in progress

// <HTTP call finishes>

const userPromise4 = cachedFetchUser({ id: 1 });
// --> new user data!

Statistics

When 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:

  • hitPromise: Cache hits on pending promises
  • hitValue: Cache hits on stored values
  • miss: Cache misses
  • put: Cache puts

The number of cache accesses may be computed with:

access = stat.hitPromise + stat.hitValue + stat.miss;

As such, the hit and miss ratios may be calculated with:

hitRatio = (stat.hitPromise + stat.hitValue) / access;
missRatio = stat.miss / access;