Documentation Index
Fetch the complete documentation index at: https://mintlify.com/akbarsaputrait/ngememoize/llms.txt
Use this file to discover all available pages before exploring further.
How the cache works internally
Ngememoize uses the NgememoizeService as the central hub for managing all memoization caches. This service is provided at the root level, ensuring a single instance across your entire Angular application.
Cache storage structure
Each memoized method gets its own isolated cache, identified by a combination of the class name and method name:
// From ngememoize.service.ts:11-12
private caches: Map<string, CacheMap<any>> = new Map();
private stats: Map<string, { hits: number; misses: number }> = new Map();
The service maintains two parallel data structures:
- caches: Stores the actual cached values, keyed by cache identifier
- stats: Tracks hits and misses for each cache
Cache entry structure
Each cached value is stored with metadata:
interface CacheEntry<T> {
value: T; // The cached result
timestamp: number; // When it was cached (Date.now())
generatedKey?: string; // The readable key for callbacks
dependencies?: DependencyArray; // Optional dependency tracking
}
This metadata enables time-based expiration and tracking of when values were cached.
Getting or creating a cache
When a memoized method is called, the decorator requests a cache from the service:
// From ngememoize.service.ts:19-25
getCache<T>(identifier: string): CacheMap<T> {
if (!this.caches.has(identifier)) {
this.caches.set(identifier, new Map());
this.stats.set(identifier, { hits: 0, misses: 0 });
}
return this.caches.get(identifier) as CacheMap<T>;
}
Caches are created lazily - they only exist once the memoized method is called for the first time.
Cache invalidation strategies
Ngememoize provides multiple strategies for invalidating cached data:
Manual cache clearing
You can clear specific caches or all caches using the clearCache() method:
// From ngememoize.service.ts:31-39
clearCache(identifier?: string): void {
if (identifier) {
this.caches.delete(identifier);
this.stats.delete(identifier);
} else {
this.caches.clear();
this.stats.clear();
}
}
Example usage:
export class ProductComponent implements OnDestroy {
constructor(private memoizeService: NgememoizeService) {}
refreshProducts(): void {
// Clear only the cache for this specific method
this.memoizeService.clearCache('ProductService_getProducts');
}
ngOnDestroy(): void {
// Clear all caches when component is destroyed
this.memoizeService.clearCache();
}
}
Cache identifiers follow the pattern ClassName_methodName. Make sure to use the correct identifier when clearing specific caches.
Time-based expiration (maxAge)
The maxAge option automatically invalidates cache entries after a specified time:
export class WeatherService {
@Ngememoize({ maxAge: 300000 }) // 5 minutes in milliseconds
getCurrentWeather(city: string): Observable<Weather> {
return this.http.get<Weather>(`/api/weather/${city}`);
}
}
Here’s how it works internally:
// From ngememoize.decorator.ts:54-76
if (cached) {
if (options.maxAge && now - cached.timestamp > options.maxAge) {
// Entry has expired - delete it
cache.delete(key);
if (options.debugLabel) {
console.debug(
`[Memoize: ${options.debugLabel}] Delete Cache for key: ${key}`
);
}
memoizeService.recordCacheMiss(cacheIdentifier);
} else {
// Entry is still valid - return cached value
memoizeService.recordCacheHit(cacheIdentifier);
return cached.value;
}
}
The expiration check happens on every cache lookup. If the entry is older than maxAge milliseconds, it’s deleted and the method executes again.
Size-based eviction (maxSize)
The maxSize option limits the number of entries in the cache. When the limit is reached, the oldest entry is evicted:
export class SearchService {
@Ngememoize({ maxSize: 100 }) // Keep only the 100 most recent searches
search(query: string, filters: SearchFilters): SearchResults[] {
return this.performSearch(query, filters);
}
}
The eviction algorithm:
// From ngememoize.decorator.ts:83-88
if (options.maxSize && cache.size >= options.maxSize) {
const oldestKey = Array.from(cache.entries()).sort(
([, a], [, b]) => a.timestamp - b.timestamp
)[0][0];
cache.delete(oldestKey);
}
This ensures your cache doesn’t grow unbounded, which is especially important for:
- Methods with high argument variability
- Long-running applications
- Memory-constrained environments
The LRU (Least Recently Used) eviction happens before storing a new entry. The cache sorts all entries by timestamp and removes the oldest one.
Cache statistics and monitoring
Recording hits and misses
Every cache access is tracked automatically:
// From ngememoize.service.ts:45-61
recordCacheHit(identifier: string): void {
const stats = this.stats.get(identifier);
if (stats) {
stats.hits++;
}
}
recordCacheMiss(identifier: string): void {
const stats = this.stats.get(identifier);
if (stats) {
stats.misses++;
}
}
Getting cache statistics
You can retrieve comprehensive statistics for all caches:
// From ngememoize.service.ts:67-84
getCacheStats(): Record<string, CacheStats> {
const stats: Record<string, CacheStats> = {};
this.caches.forEach((cache, key) => {
const entries = Array.from(cache.values());
const cacheStats = this.stats.get(key) || { hits: 0, misses: 0 };
const total = cacheStats.hits + cacheStats.misses;
stats[key] = {
size: cache.size,
lastAccessed: Math.max(...entries.map(e => e.timestamp)),
hitRate: total ? cacheStats.hits / total : 0,
missRate: total ? cacheStats.misses / total : 0,
};
});
return stats;
}
The CacheStats interface provides:
- size: Current number of entries in the cache
- lastAccessed: Timestamp of the most recent cache entry
- hitRate: Percentage of requests served from cache (0-1)
- missRate: Percentage of requests that required computation (0-1)
Example usage:
export class CacheMonitorComponent implements OnInit {
cacheStats: Record<string, CacheStats> = {};
constructor(private memoizeService: NgememoizeService) {}
ngOnInit(): void {
this.cacheStats = this.memoizeService.getCacheStats();
}
displayStats(): void {
Object.entries(this.cacheStats).forEach(([identifier, stats]) => {
console.log(`Cache: ${identifier}`);
console.log(` Size: ${stats.size}`);
console.log(` Hit Rate: ${(stats.hitRate * 100).toFixed(2)}%`);
console.log(` Miss Rate: ${(stats.missRate * 100).toFixed(2)}%`);
});
}
}
Debug logging
Use the debugLabel option to log cache activity to the console:
export class UserService {
@Ngememoize({ debugLabel: 'getUserById' })
getUserById(id: string): User {
return this.users.find(u => u.id === id);
}
}
This produces console output:
// From ngememoize.decorator.ts:48-52
if (options.debugLabel) {
console.debug(
`[Memoize: ${options.debugLabel}] Cache ${cacheStatus} for key: ${key}`
);
}
Output example:
[Memoize: getUserById] Cache miss for key: 453829
[Memoize: getUserById] Cache hit for key: 453829
[Memoize: getUserById] Delete Cache for key: 453829
Accessing all caches
For advanced use cases, you can retrieve all cache instances:
// From ngememoize.service.ts:90-96
getAllCache(): Record<string, CacheMap<any>> {
const allCaches: Record<string, CacheMap<any>> = {};
this.caches.forEach((cache, key) => {
allCaches[key] = cache;
});
return allCaches;
}
This allows you to:
- Inspect cache contents for debugging
- Implement custom eviction strategies
- Export cache data for analysis
- Manually manipulate cache entries
export class CacheDebugService {
constructor(private memoizeService: NgememoizeService) {}
inspectCache(identifier: string): void {
const allCaches = this.memoizeService.getAllCache();
const cache = allCaches[identifier];
if (cache) {
console.log(`Cache entries for ${identifier}:`);
cache.forEach((entry, key) => {
console.log(` Key: ${key}`);
console.log(` Value:`, entry.value);
console.log(` Cached at:`, new Date(entry.timestamp));
});
}
}
}
Best practices
-
Combine strategies: Use both
maxAge and maxSize for optimal cache management:
@Ngememoize({ maxAge: 300000, maxSize: 50 })
-
Monitor in development: Always use
debugLabel during development to verify cache behavior:
@Ngememoize({ debugLabel: 'myMethod', maxAge: 5000 })
-
Clear caches strategically: Clear caches when underlying data changes:
saveUser(user: User): void {
this.http.post('/api/users', user).subscribe(() => {
this.memoizeService.clearCache('UserService_getUsers');
});
}
-
Track statistics in production: Use
getCacheStats() to identify optimization opportunities and verify that memoization is providing value.