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 🙏

© 2025 – Pkg Stats / Ryan Hefner

promise-cachex

v1.7.0

Published

A simple promise cache

Downloads

34

Readme

PromiseCacheX

🚀 High-Performance Promise-Based Caching for JavaScript & TypeScript

PromiseCacheX is a lightweight caching library designed to store and manage asynchronous promises and synchronous values efficiently. It eliminates redundant requests, prevents race conditions, and automatically cleans up expired cache entries.


📚 Installation

npm install promise-cachex

🔧 Usage

import { PromiseCacheX } from "promise-cachex";

const cache = new PromiseCacheX({ ttl: 5000, cleanupInterval: 2000 }); // 5s TTL, cleanup every 2s

async function fetchData() {
  return new Promise((resolve) =>
    setTimeout(() => resolve("cached data"), 1000)
  );
}

(async () => {
  const result1 = await cache.get("key1", fetchData, { ttl: 5000 });
  console.log(result1); // 'cached data'

  const result2 = await cache.get("key1", fetchData, { ttl: 5000 });
  console.log(result2); // Returns cached value immediately
})();

// Supports caching synchronous values too
cache.get("key2", "static value");
console.log(await cache.get("key2", "static value")); // 'static value'

⚡ Features

Promise-Aware – Stores and returns pending promises to avoid duplicate calls. ✅ Supports Both Async and Sync Values – Cache promises, async functions, sync functions, or direct values. ✅ TTL Expiry – Items automatically expire after a configurable time. ✅ Automatic Cleanup – Removes expired entries at a regular interval. ✅ Manual Deletion – Allows explicit cache clearing when needed. ✅ Error Handling – Removes failed promises from the cache. ✅ Efficient & Fast – Optimized for speed and low memory overhead.


🐜 API

constructor(options?: CacheOptions)

Creates a new instance of PromiseCacheX.

| Option | Type | Default | Description | | ----------------- | -------- | -------------------- | ------------------------------------------------- | | ttl | number | 3600000 (1 hour) | Default TTL in milliseconds. 0 means no TTL. | | cleanupInterval | number | 300000 (5 minutes) | Interval in milliseconds to remove expired items. |


get<T>(key: string, fetcherOrPromise: FetchOrPromise<T>, options?: ItemOptions): Promise<T>

Retrieves a cached value or fetches and caches it if not available.

| Option | Type | Default | Description | | ------ | -------- | --------- | ------------------------------------------ | | ttl | number | Cache TTL | TTL for the cached item. 0 means no TTL. |

FetchOrPromise can be:

  • An async function returning a promise (() => Promise<T>)
  • A synchronous function returning a value (() => T)
  • A direct promise (Promise<T>)
  • A direct value (T)
// Caching an async function
const result = await cache.get("key1", async () => "value", { ttl: 5000 });

// Caching a synchronous function
const syncResult = await cache.get("key2", () => "sync value");

// Caching a direct promise
const promiseResult = await cache.get(
  "key3",
  Promise.resolve("promised value")
);

// Caching a direct value
const directResult = await cache.get("key4", "direct value");

set<T>(key: string, value: T | Promise<T>, options?: ItemOptions): void

Sets a value in the cache.

cache.set("key1", "value1", { ttl: 5000 });

delete(key: string): void

Removes a specific key from the cache.

cache.delete("key1");

clear(): void

Clears all cached entries.

cache.clear();

size(): number

Returns the number of cached items.

console.log(cache.size());

keys(): string[]

Returns an array of cached keys.

console.log(cache.keys());

has(key: string): boolean

Checks if a key exists in the cache.

console.log(cache.has("key1"));

🔤 Typing Modes: Strict vs Loose (Generics)

PromiseCacheX lets you choose between strict, single-type caches and a loose, multi-type cache—and still allows per-call type parameters on get/set.

How it works

The class is generic: PromiseCacheX<T = unknown>.

  • If you omit T, the cache runs in loose mode (accepts mixed value types).
  • If you provide T, the cache runs in strict mode (all values must conform to T).
  • You can still provide a type argument to get<U>() and set<U>(), but it is constrained so that U must extend the cache’s type.

Method signatures (simplified):

class PromiseCacheX<T = unknown> {
  get<U extends T = T>(
    key: string,
    fetcherOrPromise: (() => Promise<U> | U) | Promise<U> | U,
    options?: { ttl?: number }
  ): Promise<U>;

  set<U extends T>(
    key: string,
    value: U | Promise<U>,
    options?: { ttl?: number }
  ): void;
}

Note: When T is omitted, the library treats it as “loose” so mixed types are allowed. When T is provided, U is constrained to that type.


🧰 Loose mode (no generic) — store multiple types

When you don’t pass a generic, you can mix types freely. You may still annotate each call for clarity.

import { PromiseCacheX } from "promise-cachex";

const loose = new PromiseCacheX(); // T omitted → loose mode

// Store different types
await loose.get<number>("n1", 42);
await loose.get<string>("s1", () => "hello");
await loose.get<{ id: string }>("u1", Promise.resolve({ id: "abc" }));

// All OK — loose mode accepts them

Use this for a shared utility cache with heterogeneous values.


🔒 Strict mode (typed cache) — enforce one value type

Provide T to restrict the cache to a single type. Per-call generics on get/set must extend that type.

type User = { id: number; name: string };

const strict = new PromiseCacheX<User>(); // typed cache

// ✅ OK: value matches `User`
await strict.get<User>("u:1", () => ({ id: 1, name: "Ana" }));

// ❌ Error: `string` does not extend `User`
// await strict.get<string>("bad", "oops");

// ✅ OK: promise of `User`
strict.set("u:2", Promise.resolve({ id: 2, name: "Ion" }));

This is ideal for domain caches (e.g., Users, Products) where consistency matters.


🎯 Narrowing inside strict mode

Because U extends T, you can narrow on a call when it’s safe:

type User = { id: number; name: string };
type MaybeUser = User | null;

const cache = new PromiseCacheX<MaybeUser>();

// ✅ OK: `User` is a subtype of `User | null`
const u = await cache.get<User>("u:1", async () => ({ id: 1, name: "Ana" }));

// ✅ Also OK: storing `null`
await cache.get<MaybeUser>("u:2", null);

// ❌ Error: `string` not assignable to `User | null`
// await cache.get<string>("bad", "nope");

Tip: Using unions like User | null lets you express cacheable absence while keeping strong typing.


✅ When to use which

  • Loose mode (omit T): quick utility cache, heterogeneous values, prototyping.
  • Strict mode (PromiseCacheX<T>): domain caches with strong guarantees and easier refactors.

📊 Benchmark Results

Here are the latest performance benchmarks for PromiseCacheX:

Operations Performance

| Task | Latency Avg (ns) | Throughput Avg (ops/s) | | ------------------------------------ | ---------------- | ---------------------- | | Cache 1,000 Keys | 216,853 | 4,824 | | Cache 10,000 Keys | 2,213,626 | 461 | | Retrieve Cached Values (1,000 keys) | 221,039 | 4,756 | | Retrieve Cached Values (10,000 keys) | 2,136,370 | 476 |

Memory & CPU Usage

| Action | Memory (MB) | CPU Time (ms) | | ---------------------------- | ----------- | ------------- | | After caching 1,000 keys | 153.22 | 21,296 | | After caching 10,000 keys | 208.22 | 39,687 | | After retrieving 1,000 keys | 206.88 | 60,765 | | After retrieving 10,000 keys | 271.31 | 83,984 |


🔥 Why Use PromiseCacheX?

  • 🏆 Prevents duplicate async requests (efficient shared promises)
  • Fast and lightweight (optimized caching)
  • 🛡 Ensures memory efficiency (auto-expiring cache)
  • 🔥 Great for API calls, database queries, and computations
  • 🌟 Supports both async and sync values (no need for multiple caching libraries)

📜 License

MIT License.

🚀 Try PromiseCacheX today!