frontend
Cache with Time Limit in JavaScript
January 24, 2026
Cache with Time Limit in JavaScript
Overview
A time-limited cache is a caching mechanism where stored values automatically expire after a specified duration. This is useful for storing temporary data, API responses, computed values, and any data that has a limited validity period.
Basic Implementation
var TimeLimitedCache = function () {
this.cache = new Map();
};
/**
* @param {number} key
* @param {number} value
* @param {number} duration time until expiration in ms
* @return {boolean} if un-expired key already existed
*/
TimeLimitedCache.prototype.set = function (key, value, duration) {
const exists = this.cache.has(key) && this.cache.get(key).expiry > Date.now();
this.cache.set(key, {
value,
expiry: Date.now() + duration,
});
return exists;
};
/**
* @param {number} key
* @return {number} value associated with key
*/
TimeLimitedCache.prototype.get = function (key) {
if (!this.cache.has(key)) return -1;
const { value, expiry } = this.cache.get(key);
if (expiry > Date.now()) {
return value;
}
this.cache.delete(key);
return -1;
};
/**
* @return {number} count of non-expired keys
*/
TimeLimitedCache.prototype.count = function () {
let count = 0;
for (let [key, { value, expiry }] of this.cache.entries()) {
if (expiry > Date.now()) {
count += 1;
} else {
this.cache.delete(key);
}
}
return count;
};
// Usage
const timeLimitedCache = new TimeLimitedCache();
timeLimitedCache.set(1, 42, 1000);
timeLimitedCache.set(2, 40, 1000);
timeLimitedCache.set(3, 30, 1000);
console.log(timeLimitedCache.get(1)); // 42
console.log(timeLimitedCache.get(2)); // 40
console.log(timeLimitedCache.get(3)); // 30
console.log(timeLimitedCache.count()); // 3
Class-Based Implementation
class TimeLimitedCache {
constructor() {
this.cache = new Map();
this.cleanupInterval = null;
}
set(key, value, duration) {
const exists = this.has(key);
const expiry = Date.now() + duration;
this.cache.set(key, { value, expiry });
// Start cleanup if not already running
if (!this.cleanupInterval) {
this.startCleanup();
}
return exists;
}
get(key) {
const item = this.cache.get(key);
if (!item) {
return -1;
}
if (item.expiry > Date.now()) {
return item.value;
}
// Expired, remove it
this.cache.delete(key);
return -1;
}
has(key) {
const item = this.cache.get(key);
if (!item) return false;
if (item.expiry > Date.now()) {
return true;
}
this.cache.delete(key);
return false;
}
delete(key) {
return this.cache.delete(key);
}
clear() {
this.cache.clear();
this.stopCleanup();
}
count() {
let count = 0;
const now = Date.now();
for (const [key, item] of this.cache.entries()) {
if (item.expiry > now) {
count++;
} else {
this.cache.delete(key);
}
}
return count;
}
startCleanup() {
this.cleanupInterval = setInterval(() => {
this.cleanup();
}, 60000); // Clean every minute
}
stopCleanup() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = null;
}
}
cleanup() {
const now = Date.now();
for (const [key, item] of this.cache.entries()) {
if (item.expiry <= now) {
this.cache.delete(key);
}
}
// Stop cleanup if cache is empty
if (this.cache.size === 0) {
this.stopCleanup();
}
}
getAll() {
const result = {};
const now = Date.now();
for (const [key, item] of this.cache.entries()) {
if (item.expiry > now) {
result[key] = item.value;
} else {
this.cache.delete(key);
}
}
return result;
}
}
Advanced Features
1. Cache with Callbacks
class TimeLimitedCacheWithCallbacks extends TimeLimitedCache {
constructor(options = {}) {
super();
this.onExpire = options.onExpire || null;
this.onSet = options.onSet || null;
}
set(key, value, duration) {
const existed = super.set(key, value, duration);
if (this.onSet) {
this.onSet(key, value, duration, existed);
}
// Schedule expiration callback
if (this.onExpire) {
setTimeout(() => {
if (this.get(key) === -1) {
this.onExpire(key, value);
}
}, duration);
}
return existed;
}
}
2. Cache with Statistics
class TimeLimitedCacheWithStats extends TimeLimitedCache {
constructor() {
super();
this.stats = {
hits: 0,
misses: 0,
sets: 0,
expires: 0
};
}
get(key) {
const result = super.get(key);
if (result === -1) {
this.stats.misses++;
} else {
this.stats.hits++;
}
return result;
}
set(key, value, duration) {
const existed = super.set(key, value, duration);
this.stats.sets++;
if (!existed) {
// Track expiration
setTimeout(() => {
this.stats.expires++;
}, duration);
}
return existed;
}
getStats() {
return {
...this.stats,
hitRate: this.stats.hits / (this.stats.hits + this.stats.misses) || 0,
size: this.count()
};
}
}
3. Cache with Size Limit
class TimeLimitedCacheWithSizeLimit extends TimeLimitedCache {
constructor(maxSize = 100) {
super();
this.maxSize = maxSize;
this.accessOrder = new Map(); // For LRU eviction
}
set(key, value, duration) {
// Evict if at capacity
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
this.evictLRU();
}
this.accessOrder.set(key, Date.now());
return super.set(key, value, duration);
}
get(key) {
const result = super.get(key);
if (result !== -1) {
this.accessOrder.set(key, Date.now());
}
return result;
}
evictLRU() {
let oldestKey = null;
let oldestTime = Infinity;
for (const [key, time] of this.accessOrder.entries()) {
if (time < oldestTime && this.cache.has(key)) {
oldestTime = time;
oldestKey = key;
}
}
if (oldestKey) {
this.cache.delete(oldestKey);
this.accessOrder.delete(oldestKey);
}
}
}
Use Cases
1. API Response Caching
const apiCache = new TimeLimitedCache();
async function fetchWithCache(url) {
// Check cache first
const cached = apiCache.get(url);
if (cached !== -1) {
return cached;
}
// Fetch and cache
const response = await fetch(url).then(r => r.json());
apiCache.set(url, response, 60000); // Cache for 1 minute
return response;
}
2. Expensive Computation Caching
const computationCache = new TimeLimitedCache();
function expensiveComputation(input) {
const cached = computationCache.get(input);
if (cached !== -1) {
return cached;
}
const result = performExpensiveOperation(input);
computationCache.set(input, result, 300000); // Cache for 5 minutes
return result;
}
3. Session Data
const sessionCache = new TimeLimitedCache();
function setSessionData(userId, data) {
sessionCache.set(userId, data, 3600000); // 1 hour
}
function getSessionData(userId) {
return sessionCache.get(userId);
}
Best Practices
- Automatic Cleanup: Implement periodic cleanup to remove expired entries
- Memory Management: Limit cache size to prevent memory issues
- Error Handling: Handle cases where cache operations fail
- Statistics: Track cache performance (hits, misses, etc.)
- Expiration Callbacks: Use callbacks to handle expired entries
- Thread Safety: Consider concurrency if used in multi-threaded environments
Performance Considerations
- Cleanup Frequency: Balance cleanup frequency with performance
- Map vs Object: Use Map for better performance with dynamic keys
- Lazy Expiration: Check expiration on access, not proactively
- Size Limits: Implement size limits to prevent memory issues
Real-World Example
class APICache {
constructor() {
this.cache = new TimeLimitedCache();
}
async get(url, options = {}) {
const { ttl = 60000, forceRefresh = false } = options;
const cacheKey = `${url}-${JSON.stringify(options)}`;
if (!forceRefresh) {
const cached = this.cache.get(cacheKey);
if (cached !== -1) {
return cached;
}
}
const response = await fetch(url, options).then(r => r.json());
this.cache.set(cacheKey, response, ttl);
return response;
}
clear() {
this.cache.clear();
}
getStats() {
return {
size: this.cache.count(),
cache: this.cache.getAll()
};
}
}
// Usage
const apiCache = new APICache();
const data = await apiCache.get('/api/users', { ttl: 300000 });