forked from priyankashrama/JavaScript-Program
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmemoize.js
More file actions
94 lines (85 loc) · 2.52 KB
/
Copy pathmemoize.js
File metadata and controls
94 lines (85 loc) · 2.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
* memoize.js
* Generic memoize function with optional TTL and support for async functions.
*
* Usage:
* const fastFib = memoize(async (n) => { ... }, { ttl: 5000 });
* await fastFib(10);
*/
function isPromise(obj) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
function defaultSerializeArgs(args) {
try {
return JSON.stringify(args);
} catch {
// fallback: toString join
return args.map(a => String(a)).join('|');
}
}
/**
* memoize(fn, options)
* options:
* - ttl: milliseconds to keep cache entries (optional)
* - serialize: function(argsArray) => string (optional)
* - maxSize: maximum number of cache entries (optional; LRU eviction)
*/
function memoize(fn, options = {}) {
const { ttl = 0, serialize = defaultSerializeArgs, maxSize = Infinity } = options;
const cache = new Map(); // key -> { value, expiresAt, lastUsed }
function setEntry(key, value) {
const expiresAt = ttl > 0 ? Date.now() + ttl : Infinity;
cache.set(key, { value, expiresAt, lastUsed: Date.now() });
// enforce maxSize (simple LRU eviction)
if (cache.size > maxSize) {
// find least recently used
let lruKey = null;
let lruTime = Infinity;
for (const [k, v] of cache) {
if (v.lastUsed < lruTime) {
lruTime = v.lastUsed;
lruKey = k;
}
}
if (lruKey !== null) cache.delete(lruKey);
}
}
function getEntry(key) {
const entry = cache.get(key);
if (!entry) return undefined;
// expired?
if (entry.expiresAt !== Infinity && Date.now() > entry.expiresAt) {
cache.delete(key);
return undefined;
}
entry.lastUsed = Date.now();
return entry.value;
}
return function memoized(...args) {
const key = serialize(args);
const existing = getEntry(key);
if (existing !== undefined) {
return existing;
}
try {
const result = fn.apply(this, args);
if (isPromise(result)) {
// store the pending promise to dedupe concurrent calls
setEntry(key, result);
// if promise rejects, remove cache so future calls can retry
result.catch(() => {
// only remove if the cached value is the same promise
const cur = cache.get(key);
if (cur && cur.value === result) cache.delete(key);
});
return result;
} else {
setEntry(key, result);
return result;
}
} catch (err) {
throw err;
}
};
}
module.exports = memoize;