@taicode/common-base 1.7.3 → 1.7.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/flow-queue/flow-queue.d.ts +79 -11
- package/output/flow-queue/flow-queue.d.ts.map +1 -1
- package/output/flow-queue/flow-queue.js +115 -42
- package/output/flow-queue/flow-queue.test.js +182 -11
- package/package.json +1 -1
- package/output/events/disposer.d.ts +0 -6
- package/output/events/disposer.d.ts.map +0 -1
- package/output/events/disposer.js +0 -19
- package/output/events/disposer.test.d.ts +0 -2
- package/output/events/disposer.test.d.ts.map +0 -1
- package/output/events/disposer.test.js +0 -192
- package/output/events/event-emitter.d.ts +0 -33
- package/output/events/event-emitter.d.ts.map +0 -1
- package/output/events/event-emitter.js +0 -66
- package/output/events/event-emitter.test.d.ts +0 -2
- package/output/events/event-emitter.test.d.ts.map +0 -1
- package/output/events/event-emitter.test.js +0 -213
- package/output/events/index.d.ts +0 -3
- package/output/events/index.d.ts.map +0 -1
- package/output/events/index.js +0 -3
|
@@ -183,24 +183,98 @@ describe('FlowQueue', () => {
|
|
|
183
183
|
});
|
|
184
184
|
});
|
|
185
185
|
describe('并发处理', () => {
|
|
186
|
-
it('
|
|
186
|
+
it('应该支持配置的并发数量', async () => {
|
|
187
187
|
const inputs = [
|
|
188
188
|
{ id: 1, data: 'test1' },
|
|
189
|
-
{ id: 2, data: 'test2' }
|
|
189
|
+
{ id: 2, data: 'test2' },
|
|
190
|
+
{ id: 3, data: 'test3' },
|
|
191
|
+
{ id: 4, data: 'test4' }
|
|
190
192
|
];
|
|
191
|
-
|
|
193
|
+
let processingCount = 0;
|
|
194
|
+
let maxConcurrentProcessing = 0;
|
|
195
|
+
// 模拟较慢的处理器,用来测试并发
|
|
192
196
|
mockProcessor = vi.fn().mockImplementation(async (input) => {
|
|
197
|
+
processingCount++;
|
|
198
|
+
maxConcurrentProcessing = Math.max(maxConcurrentProcessing, processingCount);
|
|
199
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
200
|
+
processingCount--;
|
|
201
|
+
return { id: input.id, result: `processed_${input.data}`, timestamp: Date.now() };
|
|
202
|
+
});
|
|
203
|
+
// 创建支持 3 个并发的队列
|
|
204
|
+
flowQueue = new FlowQueue({
|
|
205
|
+
retry: { maxRetries: 3 },
|
|
206
|
+
concurrency: 3
|
|
207
|
+
}, mockProcessor);
|
|
208
|
+
// 添加所有输入
|
|
209
|
+
flowQueue.input(...inputs);
|
|
210
|
+
flowQueue.closeInput();
|
|
211
|
+
await flowQueue.awaitCompletion();
|
|
212
|
+
// 验证结果
|
|
213
|
+
expect(mockProcessor).toHaveBeenCalledTimes(4);
|
|
214
|
+
expect(flowQueue.successOutputs.size).toBe(4);
|
|
215
|
+
expect(maxConcurrentProcessing).toBe(3); // 应该达到配置的并发数
|
|
216
|
+
});
|
|
217
|
+
it('默认应该使用并发数 1(顺序处理)', async () => {
|
|
218
|
+
const inputs = [
|
|
219
|
+
{ id: 1, data: 'test1' },
|
|
220
|
+
{ id: 2, data: 'test2' },
|
|
221
|
+
{ id: 3, data: 'test3' }
|
|
222
|
+
];
|
|
223
|
+
let processingCount = 0;
|
|
224
|
+
let maxConcurrentProcessing = 0;
|
|
225
|
+
const processingOrder = [];
|
|
226
|
+
mockProcessor = vi.fn().mockImplementation(async (input) => {
|
|
227
|
+
processingCount++;
|
|
228
|
+
maxConcurrentProcessing = Math.max(maxConcurrentProcessing, processingCount);
|
|
229
|
+
processingOrder.push(input.id);
|
|
193
230
|
await new Promise(resolve => setTimeout(resolve, 30));
|
|
231
|
+
processingCount--;
|
|
194
232
|
return { id: input.id, result: `processed_${input.data}`, timestamp: Date.now() };
|
|
195
233
|
});
|
|
234
|
+
// 使用默认配置(并发数应该为 1)
|
|
196
235
|
flowQueue = new FlowQueue({ retry: { maxRetries: 3 } }, mockProcessor);
|
|
197
|
-
|
|
198
|
-
flowQueue.
|
|
199
|
-
flowQueue.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
expect(
|
|
203
|
-
|
|
236
|
+
flowQueue.input(...inputs);
|
|
237
|
+
flowQueue.closeInput();
|
|
238
|
+
await flowQueue.awaitCompletion();
|
|
239
|
+
// 验证顺序处理
|
|
240
|
+
expect(maxConcurrentProcessing).toBe(1);
|
|
241
|
+
expect(processingOrder).toEqual([1, 2, 3]); // 应该按顺序处理
|
|
242
|
+
});
|
|
243
|
+
it('应该在达到并发限制时等待', async () => {
|
|
244
|
+
const inputs = [
|
|
245
|
+
{ id: 1, data: 'test1' },
|
|
246
|
+
{ id: 2, data: 'test2' },
|
|
247
|
+
{ id: 3, data: 'test3' },
|
|
248
|
+
{ id: 4, data: 'test4' },
|
|
249
|
+
{ id: 5, data: 'test5' }
|
|
250
|
+
];
|
|
251
|
+
let activeProcessing = 0;
|
|
252
|
+
let maxActiveProcessing = 0;
|
|
253
|
+
const processStartTimes = [];
|
|
254
|
+
mockProcessor = vi.fn().mockImplementation(async (input) => {
|
|
255
|
+
processStartTimes.push(Date.now());
|
|
256
|
+
activeProcessing++;
|
|
257
|
+
maxActiveProcessing = Math.max(maxActiveProcessing, activeProcessing);
|
|
258
|
+
await new Promise(resolve => setTimeout(resolve, 80));
|
|
259
|
+
activeProcessing--;
|
|
260
|
+
return { id: input.id, result: `processed_${input.data}`, timestamp: Date.now() };
|
|
261
|
+
});
|
|
262
|
+
// 创建并发数为 2 的队列
|
|
263
|
+
flowQueue = new FlowQueue({
|
|
264
|
+
retry: { maxRetries: 3 },
|
|
265
|
+
concurrency: 2
|
|
266
|
+
}, mockProcessor);
|
|
267
|
+
const startTime = Date.now();
|
|
268
|
+
flowQueue.input(...inputs);
|
|
269
|
+
flowQueue.closeInput();
|
|
270
|
+
await flowQueue.awaitCompletion();
|
|
271
|
+
// 验证并发限制生效
|
|
272
|
+
expect(maxActiveProcessing).toBe(2);
|
|
273
|
+
expect(flowQueue.successOutputs.size).toBe(5);
|
|
274
|
+
// 验证第3个任务等待了前面任务完成
|
|
275
|
+
// 由于并发数为2,第3个任务应该在第一批任务完成后才开始
|
|
276
|
+
const totalTime = Date.now() - startTime;
|
|
277
|
+
expect(totalTime).toBeGreaterThan(150); // 至少需要两轮处理
|
|
204
278
|
});
|
|
205
279
|
});
|
|
206
280
|
describe('边界情况', () => {
|
|
@@ -676,7 +750,7 @@ describe('FlowQueue', () => {
|
|
|
676
750
|
expect(flowQueue['processedInputs'].length).toBe(0);
|
|
677
751
|
expect(flowQueue.successOutputs.size).toBe(0);
|
|
678
752
|
expect(flowQueue['retryCount'].size).toBe(0);
|
|
679
|
-
expect(flowQueue['
|
|
753
|
+
expect(flowQueue['processingCount']).toBe(0);
|
|
680
754
|
expect(flowQueue['isInputClosed']).toBe(false);
|
|
681
755
|
expect(flowQueue['completionPromise']).toBe(null);
|
|
682
756
|
expect(flowQueue['completionResolve']).toBe(null);
|
|
@@ -1030,4 +1104,101 @@ describe('FlowQueue', () => {
|
|
|
1030
1104
|
});
|
|
1031
1105
|
});
|
|
1032
1106
|
});
|
|
1107
|
+
describe('链式调用', () => {
|
|
1108
|
+
it('应该支持 input 方法的链式调用', async () => {
|
|
1109
|
+
const inputs = [
|
|
1110
|
+
{ id: 1, data: 'test1' },
|
|
1111
|
+
{ id: 2, data: 'test2' },
|
|
1112
|
+
{ id: 3, data: 'test3' }
|
|
1113
|
+
];
|
|
1114
|
+
mockProcessor = vi.fn().mockImplementation(async (input) => {
|
|
1115
|
+
return { id: input.id, result: `processed_${input.data}`, timestamp: Date.now() };
|
|
1116
|
+
});
|
|
1117
|
+
flowQueue = new FlowQueue({ retry: { maxRetries: 3 } }, mockProcessor);
|
|
1118
|
+
// 测试链式调用并验证返回值
|
|
1119
|
+
const returnValue = flowQueue
|
|
1120
|
+
.input(inputs[0])
|
|
1121
|
+
.input(inputs[1], inputs[2]);
|
|
1122
|
+
expect(returnValue).toBe(flowQueue); // 验证返回的是同一个实例
|
|
1123
|
+
flowQueue.closeInput();
|
|
1124
|
+
await flowQueue.awaitCompletion();
|
|
1125
|
+
expect(flowQueue.successOutputs.size).toBe(3);
|
|
1126
|
+
});
|
|
1127
|
+
it('应该支持 closeInput 方法的链式调用', async () => {
|
|
1128
|
+
const testInput = { id: 1, data: 'test' };
|
|
1129
|
+
mockProcessor = vi.fn().mockImplementation(async (input) => {
|
|
1130
|
+
return { id: input.id, result: `processed_${input.data}`, timestamp: Date.now() };
|
|
1131
|
+
});
|
|
1132
|
+
flowQueue = new FlowQueue({ retry: { maxRetries: 3 } }, mockProcessor);
|
|
1133
|
+
// 测试链式调用
|
|
1134
|
+
const returnValue = flowQueue
|
|
1135
|
+
.input(testInput)
|
|
1136
|
+
.closeInput();
|
|
1137
|
+
expect(returnValue).toBe(flowQueue);
|
|
1138
|
+
await flowQueue.awaitCompletion();
|
|
1139
|
+
expect(flowQueue.successOutputs.size).toBe(1);
|
|
1140
|
+
});
|
|
1141
|
+
it('应该支持 reset 方法的链式调用', async () => {
|
|
1142
|
+
const firstInput = { id: 1, data: 'first' };
|
|
1143
|
+
const secondInput = { id: 2, data: 'second' };
|
|
1144
|
+
mockProcessor = vi.fn().mockImplementation(async (input) => {
|
|
1145
|
+
return { id: input.id, result: `processed_${input.data}`, timestamp: Date.now() };
|
|
1146
|
+
});
|
|
1147
|
+
flowQueue = new FlowQueue({ retry: { maxRetries: 3 } }, mockProcessor);
|
|
1148
|
+
// 第一轮处理
|
|
1149
|
+
flowQueue.input(firstInput);
|
|
1150
|
+
flowQueue.closeInput();
|
|
1151
|
+
await flowQueue.awaitCompletion();
|
|
1152
|
+
expect(flowQueue.successOutputs.size).toBe(1);
|
|
1153
|
+
// 测试链式调用重置并开始新处理
|
|
1154
|
+
const returnValue = flowQueue
|
|
1155
|
+
.reset()
|
|
1156
|
+
.input(secondInput)
|
|
1157
|
+
.closeInput();
|
|
1158
|
+
expect(returnValue).toBe(flowQueue);
|
|
1159
|
+
await flowQueue.awaitCompletion();
|
|
1160
|
+
expect(flowQueue.successOutputs.size).toBe(1);
|
|
1161
|
+
expect(flowQueue.successOutputs.get(secondInput)?.result).toBe('processed_second');
|
|
1162
|
+
});
|
|
1163
|
+
it('应该支持 cancel 方法的链式调用', async () => {
|
|
1164
|
+
const testInputs = [
|
|
1165
|
+
{ id: 1, data: 'test1' },
|
|
1166
|
+
{ id: 2, data: 'test2' }
|
|
1167
|
+
];
|
|
1168
|
+
flowQueue.input(...testInputs);
|
|
1169
|
+
// 测试链式调用
|
|
1170
|
+
const returnValue = flowQueue.cancel();
|
|
1171
|
+
expect(returnValue).toBe(flowQueue);
|
|
1172
|
+
expect(flowQueue['pendingInputs'].length).toBe(0);
|
|
1173
|
+
// 等待异步完成状态更新
|
|
1174
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
1175
|
+
expect(flowQueue.isCompleted).toBe(true);
|
|
1176
|
+
});
|
|
1177
|
+
it('应该支持完整的链式调用流程', async () => {
|
|
1178
|
+
const inputs = [
|
|
1179
|
+
{ id: 1, data: 'test1' },
|
|
1180
|
+
{ id: 2, data: 'test2' },
|
|
1181
|
+
{ id: 3, data: 'test3' }
|
|
1182
|
+
];
|
|
1183
|
+
mockProcessor = vi.fn().mockImplementation(async (input) => {
|
|
1184
|
+
return { id: input.id, result: `processed_${input.data}`, timestamp: Date.now() };
|
|
1185
|
+
});
|
|
1186
|
+
flowQueue = new FlowQueue({ retry: { maxRetries: 3 } }, mockProcessor);
|
|
1187
|
+
// 测试完整的链式调用流程
|
|
1188
|
+
await flowQueue
|
|
1189
|
+
.input(inputs[0], inputs[1])
|
|
1190
|
+
.input(inputs[2])
|
|
1191
|
+
.closeInput()
|
|
1192
|
+
.awaitCompletion();
|
|
1193
|
+
expect(flowQueue.successOutputs.size).toBe(3);
|
|
1194
|
+
expect(mockProcessor).toHaveBeenCalledTimes(3);
|
|
1195
|
+
// 测试重置后的链式调用
|
|
1196
|
+
await flowQueue
|
|
1197
|
+
.reset()
|
|
1198
|
+
.input(inputs[0])
|
|
1199
|
+
.closeInput()
|
|
1200
|
+
.awaitCompletion();
|
|
1201
|
+
expect(flowQueue.successOutputs.size).toBe(1);
|
|
1202
|
+
});
|
|
1203
|
+
});
|
|
1033
1204
|
});
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"disposer.d.ts","sourceRoot":"","sources":["../../source/events/disposer.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAQ;IACnB,OAAO,CAAC,gBAAgB,CAAyB;IAGjD,WAAW,CAAC,SAAS,EAAE,MAAM,OAAO,GAAG,IAAI;IAK3C,OAAO,IAAI,IAAI;CAUhB"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export class Disposer {
|
|
2
|
-
cleanupFunctions = [];
|
|
3
|
-
// 添加清理函数
|
|
4
|
-
addDisposer(cleanupFn) {
|
|
5
|
-
this.cleanupFunctions.push(cleanupFn);
|
|
6
|
-
}
|
|
7
|
-
// 执行所有清理函数
|
|
8
|
-
dispose() {
|
|
9
|
-
for (const cleanupFn of this.cleanupFunctions) {
|
|
10
|
-
try {
|
|
11
|
-
cleanupFn();
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
console.error('Error during cleanup:', error);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
this.cleanupFunctions = []; // 清空清理函数列表
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"disposer.test.d.ts","sourceRoot":"","sources":["../../source/events/disposer.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { Disposer } from './disposer';
|
|
3
|
-
describe('Disposer', () => {
|
|
4
|
-
let disposer;
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
disposer = new Disposer();
|
|
7
|
-
});
|
|
8
|
-
describe('addDisposer', () => {
|
|
9
|
-
it('应该能够添加清理函数', () => {
|
|
10
|
-
const cleanupFn = vi.fn();
|
|
11
|
-
expect(() => {
|
|
12
|
-
disposer.addDisposer(cleanupFn);
|
|
13
|
-
}).not.toThrow();
|
|
14
|
-
});
|
|
15
|
-
it('应该能够添加多个清理函数', () => {
|
|
16
|
-
const cleanupFn1 = vi.fn();
|
|
17
|
-
const cleanupFn2 = vi.fn();
|
|
18
|
-
const cleanupFn3 = vi.fn();
|
|
19
|
-
disposer.addDisposer(cleanupFn1);
|
|
20
|
-
disposer.addDisposer(cleanupFn2);
|
|
21
|
-
disposer.addDisposer(cleanupFn3);
|
|
22
|
-
disposer.dispose();
|
|
23
|
-
expect(cleanupFn1).toHaveBeenCalledOnce();
|
|
24
|
-
expect(cleanupFn2).toHaveBeenCalledOnce();
|
|
25
|
-
expect(cleanupFn3).toHaveBeenCalledOnce();
|
|
26
|
-
});
|
|
27
|
-
it('应该能够添加返回不同类型的清理函数', () => {
|
|
28
|
-
const voidFn = vi.fn(() => { });
|
|
29
|
-
const stringFn = vi.fn(() => 'cleanup result');
|
|
30
|
-
const numberFn = vi.fn(() => 42);
|
|
31
|
-
const promiseFn = vi.fn(() => Promise.resolve('async cleanup'));
|
|
32
|
-
disposer.addDisposer(voidFn);
|
|
33
|
-
disposer.addDisposer(stringFn);
|
|
34
|
-
disposer.addDisposer(numberFn);
|
|
35
|
-
disposer.addDisposer(promiseFn);
|
|
36
|
-
disposer.dispose();
|
|
37
|
-
expect(voidFn).toHaveBeenCalled();
|
|
38
|
-
expect(stringFn).toHaveBeenCalled();
|
|
39
|
-
expect(numberFn).toHaveBeenCalled();
|
|
40
|
-
expect(promiseFn).toHaveBeenCalled();
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
describe('dispose', () => {
|
|
44
|
-
it('应该执行所有清理函数', () => {
|
|
45
|
-
const cleanupFn1 = vi.fn();
|
|
46
|
-
const cleanupFn2 = vi.fn();
|
|
47
|
-
const cleanupFn3 = vi.fn();
|
|
48
|
-
disposer.addDisposer(cleanupFn1);
|
|
49
|
-
disposer.addDisposer(cleanupFn2);
|
|
50
|
-
disposer.addDisposer(cleanupFn3);
|
|
51
|
-
disposer.dispose();
|
|
52
|
-
expect(cleanupFn1).toHaveBeenCalledOnce();
|
|
53
|
-
expect(cleanupFn2).toHaveBeenCalledOnce();
|
|
54
|
-
expect(cleanupFn3).toHaveBeenCalledOnce();
|
|
55
|
-
});
|
|
56
|
-
it('应该按添加顺序执行清理函数', () => {
|
|
57
|
-
const executionOrder = [];
|
|
58
|
-
disposer.addDisposer(() => executionOrder.push(1));
|
|
59
|
-
disposer.addDisposer(() => executionOrder.push(2));
|
|
60
|
-
disposer.addDisposer(() => executionOrder.push(3));
|
|
61
|
-
disposer.dispose();
|
|
62
|
-
expect(executionOrder).toEqual([1, 2, 3]);
|
|
63
|
-
});
|
|
64
|
-
it('应该处理清理函数中的错误', () => {
|
|
65
|
-
const workingFn1 = vi.fn();
|
|
66
|
-
const errorFn = vi.fn(() => {
|
|
67
|
-
throw new Error('Cleanup error');
|
|
68
|
-
});
|
|
69
|
-
const workingFn2 = vi.fn();
|
|
70
|
-
// 模拟 console.error
|
|
71
|
-
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
72
|
-
disposer.addDisposer(workingFn1);
|
|
73
|
-
disposer.addDisposer(errorFn);
|
|
74
|
-
disposer.addDisposer(workingFn2);
|
|
75
|
-
// dispose 不应该抛出错误
|
|
76
|
-
expect(() => {
|
|
77
|
-
disposer.dispose();
|
|
78
|
-
}).not.toThrow();
|
|
79
|
-
// 所有函数都应该被调用
|
|
80
|
-
expect(workingFn1).toHaveBeenCalled();
|
|
81
|
-
expect(errorFn).toHaveBeenCalled();
|
|
82
|
-
expect(workingFn2).toHaveBeenCalled();
|
|
83
|
-
// 错误应该被记录
|
|
84
|
-
expect(consoleSpy).toHaveBeenCalledWith('Error during cleanup:', expect.any(Error));
|
|
85
|
-
consoleSpy.mockRestore();
|
|
86
|
-
});
|
|
87
|
-
it('应该在执行后清空清理函数列表', () => {
|
|
88
|
-
const cleanupFn = vi.fn();
|
|
89
|
-
disposer.addDisposer(cleanupFn);
|
|
90
|
-
disposer.dispose();
|
|
91
|
-
expect(cleanupFn).toHaveBeenCalledOnce();
|
|
92
|
-
// 再次调用 dispose 不应该执行任何函数
|
|
93
|
-
disposer.dispose();
|
|
94
|
-
expect(cleanupFn).toHaveBeenCalledOnce();
|
|
95
|
-
});
|
|
96
|
-
it('应该能够在清空后重新添加清理函数', () => {
|
|
97
|
-
const firstCleanup = vi.fn();
|
|
98
|
-
const secondCleanup = vi.fn();
|
|
99
|
-
disposer.addDisposer(firstCleanup);
|
|
100
|
-
disposer.dispose();
|
|
101
|
-
disposer.addDisposer(secondCleanup);
|
|
102
|
-
disposer.dispose();
|
|
103
|
-
expect(firstCleanup).toHaveBeenCalledOnce();
|
|
104
|
-
expect(secondCleanup).toHaveBeenCalledOnce();
|
|
105
|
-
});
|
|
106
|
-
it('应该在没有清理函数时安全执行', () => {
|
|
107
|
-
expect(() => {
|
|
108
|
-
disposer.dispose();
|
|
109
|
-
}).not.toThrow();
|
|
110
|
-
});
|
|
111
|
-
it('应该能够多次调用 dispose', () => {
|
|
112
|
-
const cleanupFn = vi.fn();
|
|
113
|
-
disposer.addDisposer(cleanupFn);
|
|
114
|
-
disposer.dispose();
|
|
115
|
-
disposer.dispose();
|
|
116
|
-
disposer.dispose();
|
|
117
|
-
expect(cleanupFn).toHaveBeenCalledOnce();
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
describe('实际使用场景', () => {
|
|
121
|
-
it('应该能够清理事件监听器', () => {
|
|
122
|
-
const removeEventListener = vi.fn();
|
|
123
|
-
const unsubscribe = vi.fn();
|
|
124
|
-
disposer.addDisposer(removeEventListener);
|
|
125
|
-
disposer.addDisposer(unsubscribe);
|
|
126
|
-
disposer.dispose();
|
|
127
|
-
expect(removeEventListener).toHaveBeenCalled();
|
|
128
|
-
expect(unsubscribe).toHaveBeenCalled();
|
|
129
|
-
});
|
|
130
|
-
it('应该能够清理定时器', () => {
|
|
131
|
-
const clearTimeout = vi.fn();
|
|
132
|
-
const clearInterval = vi.fn();
|
|
133
|
-
disposer.addDisposer(() => clearTimeout(123));
|
|
134
|
-
disposer.addDisposer(() => clearInterval(456));
|
|
135
|
-
disposer.dispose();
|
|
136
|
-
expect(clearTimeout).toHaveBeenCalledWith(123);
|
|
137
|
-
expect(clearInterval).toHaveBeenCalledWith(456);
|
|
138
|
-
});
|
|
139
|
-
it('应该能够清理资源连接', () => {
|
|
140
|
-
const closeConnection = vi.fn();
|
|
141
|
-
const releaseResource = vi.fn();
|
|
142
|
-
disposer.addDisposer(() => closeConnection());
|
|
143
|
-
disposer.addDisposer(() => releaseResource());
|
|
144
|
-
disposer.dispose();
|
|
145
|
-
expect(closeConnection).toHaveBeenCalled();
|
|
146
|
-
expect(releaseResource).toHaveBeenCalled();
|
|
147
|
-
});
|
|
148
|
-
it('应该与 EventEmitter 配合使用', () => {
|
|
149
|
-
// 模拟 EventEmitter 的使用
|
|
150
|
-
const mockEventEmitter = {
|
|
151
|
-
on: vi.fn().mockReturnValue(vi.fn()), // 返回 off 函数
|
|
152
|
-
cleanup: vi.fn()
|
|
153
|
-
};
|
|
154
|
-
const offFunction1 = mockEventEmitter.on('event1', vi.fn());
|
|
155
|
-
const offFunction2 = mockEventEmitter.on('event2', vi.fn());
|
|
156
|
-
disposer.addDisposer(offFunction1);
|
|
157
|
-
disposer.addDisposer(offFunction2);
|
|
158
|
-
disposer.addDisposer(() => mockEventEmitter.cleanup());
|
|
159
|
-
disposer.dispose();
|
|
160
|
-
expect(offFunction1).toHaveBeenCalled();
|
|
161
|
-
expect(offFunction2).toHaveBeenCalled();
|
|
162
|
-
expect(mockEventEmitter.cleanup).toHaveBeenCalled();
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
describe('错误处理', () => {
|
|
166
|
-
it('应该捕获并记录同步错误', () => {
|
|
167
|
-
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
168
|
-
const error = new Error('Sync error');
|
|
169
|
-
disposer.addDisposer(() => {
|
|
170
|
-
throw error;
|
|
171
|
-
});
|
|
172
|
-
disposer.dispose();
|
|
173
|
-
expect(consoleSpy).toHaveBeenCalledWith('Error during cleanup:', error);
|
|
174
|
-
consoleSpy.mockRestore();
|
|
175
|
-
});
|
|
176
|
-
it('应该处理不同类型的错误', () => {
|
|
177
|
-
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
178
|
-
disposer.addDisposer(() => {
|
|
179
|
-
throw new TypeError('Type error');
|
|
180
|
-
});
|
|
181
|
-
disposer.addDisposer(() => {
|
|
182
|
-
throw 'String error';
|
|
183
|
-
});
|
|
184
|
-
disposer.addDisposer(() => {
|
|
185
|
-
throw { message: 'Object error' };
|
|
186
|
-
});
|
|
187
|
-
disposer.dispose();
|
|
188
|
-
expect(consoleSpy).toHaveBeenCalledTimes(3);
|
|
189
|
-
consoleSpy.mockRestore();
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
type OffFn = () => void;
|
|
2
|
-
type Listener<T = unknown> = (data: T) => void;
|
|
3
|
-
export declare class EventEmitter<Events extends object = object> {
|
|
4
|
-
private listeners;
|
|
5
|
-
constructor();
|
|
6
|
-
/**
|
|
7
|
-
* 订阅事件
|
|
8
|
-
* @param type 事件类型
|
|
9
|
-
* @param listener 回调函数
|
|
10
|
-
*/
|
|
11
|
-
on<K extends keyof Events>(type: K, listener: Listener<Events[K]>): OffFn;
|
|
12
|
-
/**
|
|
13
|
-
* 取消订阅
|
|
14
|
-
* @param type 事件类型
|
|
15
|
-
* @param listener 要移除的回调函数(可选)
|
|
16
|
-
*/
|
|
17
|
-
off<K extends keyof Events>(type: K, listener?: Listener<Events[K]>): void;
|
|
18
|
-
/**
|
|
19
|
-
* 触发事件
|
|
20
|
-
* @param type 事件类型
|
|
21
|
-
* @param data 要传递的数据
|
|
22
|
-
*/
|
|
23
|
-
emit<K extends keyof Events>(type: K, data: Events[K]): void;
|
|
24
|
-
/**
|
|
25
|
-
* 一次性订阅
|
|
26
|
-
* @param type 事件类型
|
|
27
|
-
* @param listener 回调函数
|
|
28
|
-
*/
|
|
29
|
-
once<K extends keyof Events>(type: K, listener: Listener<Events[K]>): void;
|
|
30
|
-
cleanup(): void;
|
|
31
|
-
}
|
|
32
|
-
export {};
|
|
33
|
-
//# sourceMappingURL=event-emitter.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"event-emitter.d.ts","sourceRoot":"","sources":["../../source/events/event-emitter.ts"],"names":[],"mappings":"AAAA,KAAK,KAAK,GAAG,MAAM,IAAI,CAAA;AACvB,KAAK,QAAQ,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;AAE9C,qBAAa,YAAY,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM;IACtD,OAAO,CAAC,SAAS,CAAsD;;IAUvE;;;;OAIG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;IASzE;;;;OAIG;IACH,GAAG,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAc1E;;;;OAIG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI;IAQ5D;;;;OAIG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAQ1E,OAAO;CAGR"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
export class EventEmitter {
|
|
2
|
-
listeners = {};
|
|
3
|
-
constructor() {
|
|
4
|
-
this.on = this.on.bind(this);
|
|
5
|
-
this.off = this.off.bind(this);
|
|
6
|
-
this.once = this.once.bind(this);
|
|
7
|
-
this.emit = this.emit.bind(this);
|
|
8
|
-
this.cleanup = this.cleanup.bind(this);
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* 订阅事件
|
|
12
|
-
* @param type 事件类型
|
|
13
|
-
* @param listener 回调函数
|
|
14
|
-
*/
|
|
15
|
-
on(type, listener) {
|
|
16
|
-
if (!this.listeners[type]) {
|
|
17
|
-
this.listeners[type] = [];
|
|
18
|
-
}
|
|
19
|
-
this.listeners[type].push(listener);
|
|
20
|
-
return () => this.off(type, listener);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* 取消订阅
|
|
24
|
-
* @param type 事件类型
|
|
25
|
-
* @param listener 要移除的回调函数(可选)
|
|
26
|
-
*/
|
|
27
|
-
off(type, listener) {
|
|
28
|
-
if (!this.listeners[type])
|
|
29
|
-
return;
|
|
30
|
-
if (!listener) {
|
|
31
|
-
// 移除该事件的所有监听器
|
|
32
|
-
delete this.listeners[type];
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
// 移除特定监听器
|
|
36
|
-
this.listeners[type] = this.listeners[type].filter((fn) => fn !== listener);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* 触发事件
|
|
41
|
-
* @param type 事件类型
|
|
42
|
-
* @param data 要传递的数据
|
|
43
|
-
*/
|
|
44
|
-
emit(type, data) {
|
|
45
|
-
const listeners = this.listeners[type];
|
|
46
|
-
// 复制数组避免回调中取消订阅导致的遍历问题
|
|
47
|
-
if (listeners) {
|
|
48
|
-
listeners.slice().forEach((fn) => fn(data));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* 一次性订阅
|
|
53
|
-
* @param type 事件类型
|
|
54
|
-
* @param listener 回调函数
|
|
55
|
-
*/
|
|
56
|
-
once(type, listener) {
|
|
57
|
-
const onceWrapper = (data) => {
|
|
58
|
-
listener(data);
|
|
59
|
-
this.off(type, onceWrapper);
|
|
60
|
-
};
|
|
61
|
-
this.on(type, onceWrapper);
|
|
62
|
-
}
|
|
63
|
-
cleanup() {
|
|
64
|
-
this.listeners = {};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"event-emitter.test.d.ts","sourceRoot":"","sources":["../../source/events/event-emitter.test.ts"],"names":[],"mappings":""}
|