fast-ttl-cache 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.mjs +111 -0
- package/package.json +9 -0
- package/test.mjs +36 -0
package/index.mjs
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
|
|
2
|
+
export default class TTLCache {
|
|
3
|
+
/**
|
|
4
|
+
* 构造函数
|
|
5
|
+
* @param options 配置选项,包含ttl(过期时间)和capacity(容量)
|
|
6
|
+
*/
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.ttl = options.ttl || Infinity;
|
|
9
|
+
this.capacity = options.capacity || Infinity;
|
|
10
|
+
this.store = new Map();
|
|
11
|
+
this.head = this.tail = null;
|
|
12
|
+
this.size = 0;
|
|
13
|
+
Object.defineProperty(this, 'size', {
|
|
14
|
+
get() {
|
|
15
|
+
return this.store.size;
|
|
16
|
+
},
|
|
17
|
+
configurable: false
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 获取缓存,惰性删除
|
|
22
|
+
* @param key 缓存键
|
|
23
|
+
* @returns 如果缓存存在且未过期返回值,否则返回null
|
|
24
|
+
*/
|
|
25
|
+
getToken(key) {
|
|
26
|
+
const item = this.store.get(key);
|
|
27
|
+
if (!item) return null;
|
|
28
|
+
if (Date.now() - item.time > this.ttl) {
|
|
29
|
+
this.removeItem(key);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return item.value;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 设置缓存,包含已存在或新增
|
|
36
|
+
* @param key 缓存键
|
|
37
|
+
* @param value 缓存值
|
|
38
|
+
*/
|
|
39
|
+
put(key, value) {
|
|
40
|
+
if (this.size === 0) {
|
|
41
|
+
this.store.set(key, {
|
|
42
|
+
key,
|
|
43
|
+
value,
|
|
44
|
+
pre: null,
|
|
45
|
+
next: null,
|
|
46
|
+
time: Date.now()
|
|
47
|
+
});
|
|
48
|
+
this.head = this.tail = key;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (this.store.has(key)) {
|
|
52
|
+
const curItem = this.store.get(key);
|
|
53
|
+
curItem.value = value;
|
|
54
|
+
curItem.time = Date.now();
|
|
55
|
+
this.moveToTail(key);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const curTail = this.store.get(this.tail);
|
|
59
|
+
const newItem = {
|
|
60
|
+
key,
|
|
61
|
+
value,
|
|
62
|
+
pre: curTail,
|
|
63
|
+
next: null,
|
|
64
|
+
time: Date.now()
|
|
65
|
+
};
|
|
66
|
+
curTail.next = newItem;
|
|
67
|
+
this.store.set(key, newItem);
|
|
68
|
+
this.tail = key;
|
|
69
|
+
if (this.size > this.capacity) {
|
|
70
|
+
this.removeItem(this.head);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 移除节点
|
|
75
|
+
* @param key
|
|
76
|
+
*/
|
|
77
|
+
removeItem(key) {
|
|
78
|
+
if (!this.store.has(key)) return;
|
|
79
|
+
const curItem = this.store.get(key);
|
|
80
|
+
if (this.size === 1) {
|
|
81
|
+
this.head = this.tail = null;
|
|
82
|
+
} else if (this.head === key) {
|
|
83
|
+
this.head = curItem.next.key;
|
|
84
|
+
curItem.next.pre = null;
|
|
85
|
+
} else if (this.tail === key) {
|
|
86
|
+
this.tail = curItem.pre.key;
|
|
87
|
+
curItem.pre.next = null;
|
|
88
|
+
} else {
|
|
89
|
+
curItem.pre.next = curItem.next;
|
|
90
|
+
curItem.next.pre = curItem.pre;
|
|
91
|
+
}
|
|
92
|
+
this.store.delete(key);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 将节点移动到队尾,队尾的节点一定是最后一个更新的
|
|
96
|
+
* @param key
|
|
97
|
+
*/
|
|
98
|
+
moveToTail(key) {
|
|
99
|
+
if (!this.store.has(key)) return;
|
|
100
|
+
if (this.tail === key) return;
|
|
101
|
+
const curItem = this.store.get(key);
|
|
102
|
+
this.removeItem(key);
|
|
103
|
+
const curTail = this.store.get(this.tail);
|
|
104
|
+
curTail.next = curItem;
|
|
105
|
+
curItem.pre = curTail;
|
|
106
|
+
curItem.next = null;
|
|
107
|
+
this.tail = key;
|
|
108
|
+
this.store.set(key, curItem);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
package/package.json
ADDED
package/test.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
import TTLCache from './index.mjs';
|
|
3
|
+
|
|
4
|
+
const sleep = ms => new Promise(res => setTimeout(res, ms));
|
|
5
|
+
|
|
6
|
+
const c = new TTLCache({
|
|
7
|
+
ttl: 1000,
|
|
8
|
+
capacity: 3,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
c.put('a', 'aaaa');
|
|
12
|
+
c.put('a', 'aaaaaa');
|
|
13
|
+
|
|
14
|
+
await sleep(200);
|
|
15
|
+
c.put('b', 'bbbb');
|
|
16
|
+
|
|
17
|
+
await sleep(500);
|
|
18
|
+
c.put('c', 'cccc');
|
|
19
|
+
|
|
20
|
+
await sleep(500);
|
|
21
|
+
c.put('b', 'bbbbbb');
|
|
22
|
+
|
|
23
|
+
await sleep(600);
|
|
24
|
+
c.put('d', 'dddd');
|
|
25
|
+
|
|
26
|
+
console.log('b', c.getToken('b'));
|
|
27
|
+
|
|
28
|
+
await sleep(600);
|
|
29
|
+
c.put('e', 'eeee');
|
|
30
|
+
|
|
31
|
+
console.log(c);
|
|
32
|
+
console.log(c.store.keys());
|
|
33
|
+
console.log('b', c.getToken('b'));
|
|
34
|
+
console.log('c', c.getToken('c'));
|
|
35
|
+
console.log('a', c.getToken('a'));
|
|
36
|
+
console.log('e', c.getToken('e'));
|