@taicode/common-base 1.1.3 → 1.1.4
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/output/catch/catch.d.ts +3 -3
- package/output/catch/catch.d.ts.map +1 -1
- package/output/catch/catch.js +9 -5
- package/output/catch/catch.test.js +105 -0
- package/output/lru-cache/index.d.ts +2 -0
- package/output/lru-cache/index.d.ts.map +1 -0
- package/output/lru-cache/index.js +1 -0
- package/output/lru-cache/lru-cache.d.ts +22 -0
- package/output/lru-cache/lru-cache.d.ts.map +1 -0
- package/output/lru-cache/lru-cache.js +147 -0
- package/output/lru-cache/lru-cache.test.d.ts +2 -0
- package/output/lru-cache/lru-cache.test.d.ts.map +1 -0
- package/output/lru-cache/lru-cache.test.js +239 -0
- package/package.json +1 -1
package/output/catch/catch.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
type CatchType<F extends () => unknown> = F extends () =>
|
|
1
|
+
type CatchType<F extends () => unknown> = F extends () => PromiseLike<infer R> ? R : F extends () => infer R ? R : never;
|
|
2
2
|
/**
|
|
3
|
-
* 通用错误捕获函数,支持
|
|
3
|
+
* 通用错误捕获函数,支持 PromiseLike 和函数执行
|
|
4
4
|
* 将结果转换成 [result, error] 的形式
|
|
5
5
|
*/
|
|
6
|
-
export declare function catchIt<T>(input:
|
|
6
|
+
export declare function catchIt<T>(input: PromiseLike<T>): Promise<[T | undefined, unknown]>;
|
|
7
7
|
export declare function catchIt<F extends () => unknown>(input: F): Promise<[CatchType<F> | undefined, unknown]>;
|
|
8
8
|
export {};
|
|
9
9
|
//# sourceMappingURL=catch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catch.d.ts","sourceRoot":"","sources":["../../source/catch/catch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"catch.d.ts","sourceRoot":"","sources":["../../source/catch/catch.ts"],"names":[],"mappings":"AAKA,KAAK,SAAS,CAAC,CAAC,SAAS,MAAM,OAAO,IAAI,CAAC,SAAS,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,MAAM,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;AAExH;;;GAGG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,OAAO,CAAC,CAAC,CAAA;AAC1F,wBAAsB,OAAO,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,OAAO,CAAC,CAAC,CAAA"}
|
package/output/catch/catch.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
+
// 辅助函数:检查是否为 PromiseLike
|
|
2
|
+
function isPromiseLike(value) {
|
|
3
|
+
return value != null && typeof value === 'object' && 'then' in value && typeof value.then === 'function';
|
|
4
|
+
}
|
|
1
5
|
export async function catchIt(input) {
|
|
2
6
|
const returned = [undefined, undefined];
|
|
3
7
|
try {
|
|
4
|
-
if (input
|
|
5
|
-
// 处理
|
|
6
|
-
await input
|
|
8
|
+
if (isPromiseLike(input)) {
|
|
9
|
+
// 处理 PromiseLike
|
|
10
|
+
await Promise.resolve(input)
|
|
7
11
|
.then((result) => (returned[0] = result))
|
|
8
12
|
.catch((error) => (returned[1] = error));
|
|
9
13
|
}
|
|
10
14
|
else if (typeof input === 'function') {
|
|
11
15
|
// 处理函数
|
|
12
16
|
const result = input();
|
|
13
|
-
if (result
|
|
14
|
-
// 如果函数返回
|
|
17
|
+
if (isPromiseLike(result)) {
|
|
18
|
+
// 如果函数返回 PromiseLike,递归调用
|
|
15
19
|
return catchIt(result);
|
|
16
20
|
}
|
|
17
21
|
else {
|
|
@@ -255,4 +255,109 @@ describe('catchIt', () => {
|
|
|
255
255
|
expect(jsonError).toBeInstanceOf(SyntaxError);
|
|
256
256
|
});
|
|
257
257
|
});
|
|
258
|
+
describe('处理 PromiseLike 对象', () => {
|
|
259
|
+
it('应该正确处理自定义的 PromiseLike 对象', async () => {
|
|
260
|
+
// 创建自定义的 PromiseLike 对象
|
|
261
|
+
const customPromiseLike = {
|
|
262
|
+
then(onfulfilled, onrejected) {
|
|
263
|
+
try {
|
|
264
|
+
const result = onfulfilled?.('custom promise result');
|
|
265
|
+
return Promise.resolve(result);
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
return onrejected ? Promise.resolve(onrejected(error)) : Promise.reject(error);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
const [result, error] = await catchIt(customPromiseLike);
|
|
273
|
+
expect(result).toBe('custom promise result');
|
|
274
|
+
expect(error).toBeUndefined();
|
|
275
|
+
});
|
|
276
|
+
it('应该正确处理失败的 PromiseLike 对象', async () => {
|
|
277
|
+
const customError = new Error('custom promise error');
|
|
278
|
+
const failingPromiseLike = {
|
|
279
|
+
then(onfulfilled, onrejected) {
|
|
280
|
+
if (onrejected) {
|
|
281
|
+
return Promise.resolve(onrejected(customError));
|
|
282
|
+
}
|
|
283
|
+
return Promise.reject(customError);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
const [result, error] = await catchIt(failingPromiseLike);
|
|
287
|
+
expect(result).toBeUndefined();
|
|
288
|
+
expect(error).toBe(customError);
|
|
289
|
+
});
|
|
290
|
+
it('应该正确处理返回 PromiseLike 的函数', async () => {
|
|
291
|
+
const customPromiseLike = {
|
|
292
|
+
then(onfulfilled, onrejected) {
|
|
293
|
+
try {
|
|
294
|
+
const result = onfulfilled?.(42);
|
|
295
|
+
return Promise.resolve(result);
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
return onrejected ? Promise.resolve(onrejected(error)) : Promise.reject(error);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
const fn = () => customPromiseLike;
|
|
303
|
+
const [result, error] = await catchIt(fn);
|
|
304
|
+
expect(result).toBe(42);
|
|
305
|
+
expect(error).toBeUndefined();
|
|
306
|
+
});
|
|
307
|
+
it('应该正确处理第三方库的 PromiseLike 对象', async () => {
|
|
308
|
+
// 模拟第三方库的 PromiseLike 实现 (如 jQuery.Deferred, Bluebird 等)
|
|
309
|
+
class CustomPromise {
|
|
310
|
+
value;
|
|
311
|
+
isResolved = false;
|
|
312
|
+
isRejected = false;
|
|
313
|
+
rejectionReason;
|
|
314
|
+
constructor(executor) {
|
|
315
|
+
const resolve = (value) => {
|
|
316
|
+
this.value = value;
|
|
317
|
+
this.isResolved = true;
|
|
318
|
+
};
|
|
319
|
+
const reject = (reason) => {
|
|
320
|
+
this.rejectionReason = reason;
|
|
321
|
+
this.isRejected = true;
|
|
322
|
+
};
|
|
323
|
+
try {
|
|
324
|
+
executor(resolve, reject);
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
reject(error);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
then(onfulfilled, onrejected) {
|
|
331
|
+
if (this.isResolved && onfulfilled) {
|
|
332
|
+
try {
|
|
333
|
+
const result = onfulfilled(this.value);
|
|
334
|
+
return Promise.resolve(result);
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
return Promise.reject(error);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
else if (this.isRejected && onrejected) {
|
|
341
|
+
try {
|
|
342
|
+
const result = onrejected(this.rejectionReason);
|
|
343
|
+
return Promise.resolve(result);
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
return Promise.reject(error);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
else if (this.isRejected) {
|
|
350
|
+
return Promise.reject(this.rejectionReason);
|
|
351
|
+
}
|
|
352
|
+
return Promise.resolve(this.value);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const customPromise = new CustomPromise((resolve) => {
|
|
356
|
+
resolve('third party promise');
|
|
357
|
+
});
|
|
358
|
+
const [result, error] = await catchIt(customPromise);
|
|
359
|
+
expect(result).toBe('third party promise');
|
|
360
|
+
expect(error).toBeUndefined();
|
|
361
|
+
});
|
|
362
|
+
});
|
|
258
363
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/lru-cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LruCache } from './lru-cache';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare class LruCache<T> {
|
|
2
|
+
private cache;
|
|
3
|
+
private maxSize;
|
|
4
|
+
private head;
|
|
5
|
+
private tail;
|
|
6
|
+
constructor(maxSize?: number);
|
|
7
|
+
get(key: string): T | undefined;
|
|
8
|
+
set(key: string, value: T): void;
|
|
9
|
+
has(key: string): boolean;
|
|
10
|
+
delete(key: string): boolean;
|
|
11
|
+
get size(): number;
|
|
12
|
+
get capacity(): number;
|
|
13
|
+
clear(): void;
|
|
14
|
+
keys(): string[];
|
|
15
|
+
values(): T[];
|
|
16
|
+
entries(): Array<[string, T]>;
|
|
17
|
+
private addToHead;
|
|
18
|
+
private removeNode;
|
|
19
|
+
private moveToHead;
|
|
20
|
+
private removeTail;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=lru-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lru-cache.d.ts","sourceRoot":"","sources":["../../source/lru-cache/lru-cache.ts"],"names":[],"mappings":"AAOA,qBAAa,QAAQ,CAAC,CAAC;IACrB,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,IAAI,CAAY;gBAEZ,OAAO,GAAE,MAAY;IAgBjC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAY/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAgChC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAKzB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAY5B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAGD,IAAI,QAAQ,IAAI,MAAM,CAErB;IAGD,KAAK,IAAI,IAAI;IAOb,IAAI,IAAI,MAAM,EAAE;IAWhB,MAAM,IAAI,CAAC,EAAE;IAWb,OAAO,IAAI,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAW7B,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,UAAU;CAQnB"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
export class LruCache {
|
|
2
|
+
cache;
|
|
3
|
+
maxSize;
|
|
4
|
+
head;
|
|
5
|
+
tail;
|
|
6
|
+
constructor(maxSize = 100) {
|
|
7
|
+
if (maxSize <= 0) {
|
|
8
|
+
throw new Error('maxSize must be greater than 0');
|
|
9
|
+
}
|
|
10
|
+
this.maxSize = maxSize;
|
|
11
|
+
this.cache = new Map();
|
|
12
|
+
// 创建虚拟头尾节点
|
|
13
|
+
this.head = { key: '', value: null, prev: null, next: null };
|
|
14
|
+
this.tail = { key: '', value: null, prev: null, next: null };
|
|
15
|
+
this.head.next = this.tail;
|
|
16
|
+
this.tail.prev = this.head;
|
|
17
|
+
}
|
|
18
|
+
// 获取缓存中的数据
|
|
19
|
+
get(key) {
|
|
20
|
+
const node = this.cache.get(key);
|
|
21
|
+
if (!node) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
// 移动到头部(最近使用)
|
|
25
|
+
this.moveToHead(node);
|
|
26
|
+
return node.value;
|
|
27
|
+
}
|
|
28
|
+
// 设置缓存数据
|
|
29
|
+
set(key, value) {
|
|
30
|
+
const existingNode = this.cache.get(key);
|
|
31
|
+
if (existingNode) {
|
|
32
|
+
// 更新现有节点的值并移动到头部
|
|
33
|
+
existingNode.value = value;
|
|
34
|
+
this.moveToHead(existingNode);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// 创建新节点
|
|
38
|
+
const newNode = {
|
|
39
|
+
key,
|
|
40
|
+
value,
|
|
41
|
+
prev: null,
|
|
42
|
+
next: null
|
|
43
|
+
};
|
|
44
|
+
// 检查是否超过容量
|
|
45
|
+
if (this.cache.size >= this.maxSize) {
|
|
46
|
+
// 移除最少使用的节点(尾部)
|
|
47
|
+
const tail = this.removeTail();
|
|
48
|
+
if (tail) {
|
|
49
|
+
this.cache.delete(tail.key);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// 添加到头部
|
|
53
|
+
this.cache.set(key, newNode);
|
|
54
|
+
this.addToHead(newNode);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// 检查键是否存在
|
|
58
|
+
has(key) {
|
|
59
|
+
return this.cache.has(key);
|
|
60
|
+
}
|
|
61
|
+
// 删除指定键
|
|
62
|
+
delete(key) {
|
|
63
|
+
const node = this.cache.get(key);
|
|
64
|
+
if (!node) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
this.removeNode(node);
|
|
68
|
+
this.cache.delete(key);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
// 获取当前缓存大小
|
|
72
|
+
get size() {
|
|
73
|
+
return this.cache.size;
|
|
74
|
+
}
|
|
75
|
+
// 获取最大容量
|
|
76
|
+
get capacity() {
|
|
77
|
+
return this.maxSize;
|
|
78
|
+
}
|
|
79
|
+
// 清空缓存
|
|
80
|
+
clear() {
|
|
81
|
+
this.cache.clear();
|
|
82
|
+
this.head.next = this.tail;
|
|
83
|
+
this.tail.prev = this.head;
|
|
84
|
+
}
|
|
85
|
+
// 获取所有键(按使用频率排序,最近使用的在前)
|
|
86
|
+
keys() {
|
|
87
|
+
const keys = [];
|
|
88
|
+
let current = this.head.next;
|
|
89
|
+
while (current && current !== this.tail) {
|
|
90
|
+
keys.push(current.key);
|
|
91
|
+
current = current.next;
|
|
92
|
+
}
|
|
93
|
+
return keys;
|
|
94
|
+
}
|
|
95
|
+
// 获取所有值(按使用频率排序,最近使用的在前)
|
|
96
|
+
values() {
|
|
97
|
+
const values = [];
|
|
98
|
+
let current = this.head.next;
|
|
99
|
+
while (current && current !== this.tail) {
|
|
100
|
+
values.push(current.value);
|
|
101
|
+
current = current.next;
|
|
102
|
+
}
|
|
103
|
+
return values;
|
|
104
|
+
}
|
|
105
|
+
// 获取所有键值对
|
|
106
|
+
entries() {
|
|
107
|
+
const entries = [];
|
|
108
|
+
let current = this.head.next;
|
|
109
|
+
while (current && current !== this.tail) {
|
|
110
|
+
entries.push([current.key, current.value]);
|
|
111
|
+
current = current.next;
|
|
112
|
+
}
|
|
113
|
+
return entries;
|
|
114
|
+
}
|
|
115
|
+
// 私有方法:添加节点到头部
|
|
116
|
+
addToHead(node) {
|
|
117
|
+
node.prev = this.head;
|
|
118
|
+
node.next = this.head.next;
|
|
119
|
+
if (this.head.next) {
|
|
120
|
+
this.head.next.prev = node;
|
|
121
|
+
}
|
|
122
|
+
this.head.next = node;
|
|
123
|
+
}
|
|
124
|
+
// 私有方法:移除节点
|
|
125
|
+
removeNode(node) {
|
|
126
|
+
if (node.prev) {
|
|
127
|
+
node.prev.next = node.next;
|
|
128
|
+
}
|
|
129
|
+
if (node.next) {
|
|
130
|
+
node.next.prev = node.prev;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// 私有方法:移动节点到头部
|
|
134
|
+
moveToHead(node) {
|
|
135
|
+
this.removeNode(node);
|
|
136
|
+
this.addToHead(node);
|
|
137
|
+
}
|
|
138
|
+
// 私有方法:移除尾部节点
|
|
139
|
+
removeTail() {
|
|
140
|
+
const lastNode = this.tail.prev;
|
|
141
|
+
if (lastNode && lastNode !== this.head) {
|
|
142
|
+
this.removeNode(lastNode);
|
|
143
|
+
return lastNode;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lru-cache.test.d.ts","sourceRoot":"","sources":["../../source/lru-cache/lru-cache.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { LruCache } from './lru-cache';
|
|
3
|
+
describe('LruCache', () => {
|
|
4
|
+
describe('构造函数', () => {
|
|
5
|
+
it('应该创建默认容量为100的缓存', () => {
|
|
6
|
+
const cache = new LruCache();
|
|
7
|
+
expect(cache.capacity).toBe(100);
|
|
8
|
+
expect(cache.size).toBe(0);
|
|
9
|
+
});
|
|
10
|
+
it('应该创建指定容量的缓存', () => {
|
|
11
|
+
const cache = new LruCache(50);
|
|
12
|
+
expect(cache.capacity).toBe(50);
|
|
13
|
+
expect(cache.size).toBe(0);
|
|
14
|
+
});
|
|
15
|
+
it('应该在容量小于等于0时抛出错误', () => {
|
|
16
|
+
expect(() => new LruCache(0)).toThrow('maxSize must be greater than 0');
|
|
17
|
+
expect(() => new LruCache(-1)).toThrow('maxSize must be greater than 0');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe('基本操作', () => {
|
|
21
|
+
it('应该能设置和获取值', () => {
|
|
22
|
+
const cache = new LruCache(3);
|
|
23
|
+
cache.set('key1', 'value1');
|
|
24
|
+
expect(cache.get('key1')).toBe('value1');
|
|
25
|
+
expect(cache.size).toBe(1);
|
|
26
|
+
});
|
|
27
|
+
it('应该在键不存在时返回undefined', () => {
|
|
28
|
+
const cache = new LruCache(3);
|
|
29
|
+
expect(cache.get('nonexistent')).toBeUndefined();
|
|
30
|
+
});
|
|
31
|
+
it('应该正确检查键是否存在', () => {
|
|
32
|
+
const cache = new LruCache(3);
|
|
33
|
+
expect(cache.has('key1')).toBe(false);
|
|
34
|
+
cache.set('key1', 'value1');
|
|
35
|
+
expect(cache.has('key1')).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
it('应该能更新现有键的值', () => {
|
|
38
|
+
const cache = new LruCache(3);
|
|
39
|
+
cache.set('key1', 'value1');
|
|
40
|
+
cache.set('key1', 'updated_value1');
|
|
41
|
+
expect(cache.get('key1')).toBe('updated_value1');
|
|
42
|
+
expect(cache.size).toBe(1);
|
|
43
|
+
});
|
|
44
|
+
it('应该能删除键', () => {
|
|
45
|
+
const cache = new LruCache(3);
|
|
46
|
+
cache.set('key1', 'value1');
|
|
47
|
+
expect(cache.delete('key1')).toBe(true);
|
|
48
|
+
expect(cache.has('key1')).toBe(false);
|
|
49
|
+
expect(cache.size).toBe(0);
|
|
50
|
+
});
|
|
51
|
+
it('应该在删除不存在的键时返回false', () => {
|
|
52
|
+
const cache = new LruCache(3);
|
|
53
|
+
expect(cache.delete('nonexistent')).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it('应该能清空缓存', () => {
|
|
56
|
+
const cache = new LruCache(3);
|
|
57
|
+
cache.set('key1', 'value1');
|
|
58
|
+
cache.set('key2', 'value2');
|
|
59
|
+
cache.clear();
|
|
60
|
+
expect(cache.size).toBe(0);
|
|
61
|
+
expect(cache.has('key1')).toBe(false);
|
|
62
|
+
expect(cache.has('key2')).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('LRU 行为', () => {
|
|
66
|
+
it('应该在达到容量限制时移除最久未使用的项', () => {
|
|
67
|
+
const cache = new LruCache(2);
|
|
68
|
+
cache.set('key1', 'value1');
|
|
69
|
+
cache.set('key2', 'value2');
|
|
70
|
+
cache.set('key3', 'value3'); // 这应该移除 key1
|
|
71
|
+
expect(cache.has('key1')).toBe(false);
|
|
72
|
+
expect(cache.has('key2')).toBe(true);
|
|
73
|
+
expect(cache.has('key3')).toBe(true);
|
|
74
|
+
expect(cache.size).toBe(2);
|
|
75
|
+
});
|
|
76
|
+
it('应该在访问时更新使用顺序', () => {
|
|
77
|
+
const cache = new LruCache(2);
|
|
78
|
+
cache.set('key1', 'value1');
|
|
79
|
+
cache.set('key2', 'value2');
|
|
80
|
+
cache.get('key1'); // 使 key1 成为最近使用的
|
|
81
|
+
cache.set('key3', 'value3'); // 这应该移除 key2,而不是 key1
|
|
82
|
+
expect(cache.has('key1')).toBe(true);
|
|
83
|
+
expect(cache.has('key2')).toBe(false);
|
|
84
|
+
expect(cache.has('key3')).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
it('应该在更新现有键时保持其为最近使用', () => {
|
|
87
|
+
const cache = new LruCache(2);
|
|
88
|
+
cache.set('key1', 'value1');
|
|
89
|
+
cache.set('key2', 'value2');
|
|
90
|
+
cache.set('key1', 'updated_value1'); // 更新 key1
|
|
91
|
+
cache.set('key3', 'value3'); // 这应该移除 key2
|
|
92
|
+
expect(cache.has('key1')).toBe(true);
|
|
93
|
+
expect(cache.get('key1')).toBe('updated_value1');
|
|
94
|
+
expect(cache.has('key2')).toBe(false);
|
|
95
|
+
expect(cache.has('key3')).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('遍历方法', () => {
|
|
99
|
+
it('应该返回正确的键列表(按使用顺序)', () => {
|
|
100
|
+
const cache = new LruCache(3);
|
|
101
|
+
cache.set('key1', 'value1');
|
|
102
|
+
cache.set('key2', 'value2');
|
|
103
|
+
cache.set('key3', 'value3');
|
|
104
|
+
cache.get('key1'); // 使 key1 成为最近使用的
|
|
105
|
+
const keys = cache.keys();
|
|
106
|
+
expect(keys).toEqual(['key1', 'key3', 'key2']);
|
|
107
|
+
});
|
|
108
|
+
it('应该返回正确的值列表(按使用顺序)', () => {
|
|
109
|
+
const cache = new LruCache(3);
|
|
110
|
+
cache.set('key1', 'value1');
|
|
111
|
+
cache.set('key2', 'value2');
|
|
112
|
+
cache.set('key3', 'value3');
|
|
113
|
+
cache.get('key1'); // 使 key1 成为最近使用的
|
|
114
|
+
const values = cache.values();
|
|
115
|
+
expect(values).toEqual(['value1', 'value3', 'value2']);
|
|
116
|
+
});
|
|
117
|
+
it('应该返回正确的键值对列表(按使用顺序)', () => {
|
|
118
|
+
const cache = new LruCache(3);
|
|
119
|
+
cache.set('key1', 'value1');
|
|
120
|
+
cache.set('key2', 'value2');
|
|
121
|
+
cache.set('key3', 'value3');
|
|
122
|
+
cache.get('key1'); // 使 key1 成为最近使用的
|
|
123
|
+
const entries = cache.entries();
|
|
124
|
+
expect(entries).toEqual([
|
|
125
|
+
['key1', 'value1'],
|
|
126
|
+
['key3', 'value3'],
|
|
127
|
+
['key2', 'value2']
|
|
128
|
+
]);
|
|
129
|
+
});
|
|
130
|
+
it('应该在空缓存时返回空数组', () => {
|
|
131
|
+
const cache = new LruCache(3);
|
|
132
|
+
expect(cache.keys()).toEqual([]);
|
|
133
|
+
expect(cache.values()).toEqual([]);
|
|
134
|
+
expect(cache.entries()).toEqual([]);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe('边界情况', () => {
|
|
138
|
+
it('应该处理容量为1的缓存', () => {
|
|
139
|
+
const cache = new LruCache(1);
|
|
140
|
+
cache.set('key1', 'value1');
|
|
141
|
+
expect(cache.get('key1')).toBe('value1');
|
|
142
|
+
cache.set('key2', 'value2');
|
|
143
|
+
expect(cache.has('key1')).toBe(false);
|
|
144
|
+
expect(cache.get('key2')).toBe('value2');
|
|
145
|
+
expect(cache.size).toBe(1);
|
|
146
|
+
});
|
|
147
|
+
it('应该处理大量的操作', () => {
|
|
148
|
+
const cache = new LruCache(100);
|
|
149
|
+
// 添加200个项目
|
|
150
|
+
for (let i = 0; i < 200; i++) {
|
|
151
|
+
cache.set(`key${i}`, i);
|
|
152
|
+
}
|
|
153
|
+
expect(cache.size).toBe(100);
|
|
154
|
+
// 前100个应该被移除
|
|
155
|
+
for (let i = 0; i < 100; i++) {
|
|
156
|
+
expect(cache.has(`key${i}`)).toBe(false);
|
|
157
|
+
}
|
|
158
|
+
// 后100个应该存在
|
|
159
|
+
for (let i = 100; i < 200; i++) {
|
|
160
|
+
expect(cache.has(`key${i}`)).toBe(true);
|
|
161
|
+
expect(cache.get(`key${i}`)).toBe(i);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
it('应该处理复杂的对象类型', () => {
|
|
165
|
+
const cache = new LruCache(3);
|
|
166
|
+
const obj1 = { id: 1, name: 'test1', data: { count: 10 } };
|
|
167
|
+
const obj2 = { id: 2, name: 'test2', data: { count: 20 } };
|
|
168
|
+
cache.set('obj1', obj1);
|
|
169
|
+
cache.set('obj2', obj2);
|
|
170
|
+
expect(cache.get('obj1')).toEqual(obj1);
|
|
171
|
+
expect(cache.get('obj2')).toEqual(obj2);
|
|
172
|
+
});
|
|
173
|
+
it('应该处理null和undefined值', () => {
|
|
174
|
+
const cache = new LruCache(3);
|
|
175
|
+
cache.set('null', null);
|
|
176
|
+
cache.set('undefined', undefined);
|
|
177
|
+
expect(cache.get('null')).toBeNull();
|
|
178
|
+
expect(cache.get('undefined')).toBeUndefined();
|
|
179
|
+
expect(cache.has('null')).toBe(true);
|
|
180
|
+
expect(cache.has('undefined')).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('性能和内存', () => {
|
|
184
|
+
it('应该在删除后正确管理内存', () => {
|
|
185
|
+
const cache = new LruCache(3);
|
|
186
|
+
cache.set('key1', 'value1');
|
|
187
|
+
cache.set('key2', 'value2');
|
|
188
|
+
cache.set('key3', 'value3');
|
|
189
|
+
expect(cache.size).toBe(3);
|
|
190
|
+
cache.delete('key2');
|
|
191
|
+
expect(cache.size).toBe(2);
|
|
192
|
+
expect(cache.keys()).toEqual(['key3', 'key1']);
|
|
193
|
+
});
|
|
194
|
+
it('应该正确处理连续的相同键设置', () => {
|
|
195
|
+
const cache = new LruCache(2);
|
|
196
|
+
cache.set('key1', 'value1');
|
|
197
|
+
cache.set('key1', 'value1_updated');
|
|
198
|
+
cache.set('key1', 'value1_final');
|
|
199
|
+
expect(cache.size).toBe(1);
|
|
200
|
+
expect(cache.get('key1')).toBe('value1_final');
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
describe('实际使用场景', () => {
|
|
204
|
+
it('应该能用作函数结果缓存', () => {
|
|
205
|
+
const cache = new LruCache(3);
|
|
206
|
+
let computeCount = 0;
|
|
207
|
+
function expensiveCompute(n) {
|
|
208
|
+
const cacheKey = `compute_${n}`;
|
|
209
|
+
if (cache.has(cacheKey)) {
|
|
210
|
+
return cache.get(cacheKey);
|
|
211
|
+
}
|
|
212
|
+
computeCount++;
|
|
213
|
+
const result = n * n; // 模拟昂贵的计算
|
|
214
|
+
cache.set(cacheKey, result);
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
// 第一次计算
|
|
218
|
+
expect(expensiveCompute(5)).toBe(25);
|
|
219
|
+
expect(computeCount).toBe(1);
|
|
220
|
+
// 缓存命中
|
|
221
|
+
expect(expensiveCompute(5)).toBe(25);
|
|
222
|
+
expect(computeCount).toBe(1);
|
|
223
|
+
// 新的计算
|
|
224
|
+
expect(expensiveCompute(6)).toBe(36);
|
|
225
|
+
expect(computeCount).toBe(2);
|
|
226
|
+
});
|
|
227
|
+
it('应该能用作API响应缓存', () => {
|
|
228
|
+
const cache = new LruCache(10);
|
|
229
|
+
const mockApiResponse = {
|
|
230
|
+
data: [{ id: 1, name: 'test' }],
|
|
231
|
+
timestamp: Date.now()
|
|
232
|
+
};
|
|
233
|
+
cache.set('/api/users', mockApiResponse);
|
|
234
|
+
const cachedResponse = cache.get('/api/users');
|
|
235
|
+
expect(cachedResponse).toEqual(mockApiResponse);
|
|
236
|
+
expect(cachedResponse?.data).toHaveLength(1);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
});
|