fastevent 0.0.1 → 1.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/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/.github/workflows/publish.yaml +50 -0
- package/.vscode/launch.json +20 -0
- package/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/bench.png +0 -0
- package/dist/index.d.mts +205 -0
- package/dist/index.d.ts +205 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +34 -12
- package/readme.md +282 -0
- package/readme_cn.md +282 -0
- package/src/__benchmarks__/index.ts +3 -0
- package/src/__benchmarks__/multi-level.ts +40 -0
- package/src/__benchmarks__/sample.ts +40 -0
- package/src/__benchmarks__/wildcard.ts +41 -0
- package/src/__tests__/emit.test.ts +94 -0
- package/src/__tests__/emitAsync.test.ts +65 -0
- package/src/__tests__/isPathMatched.test.ts +205 -0
- package/src/__tests__/many.test.ts +23 -0
- package/src/__tests__/meta.test.ts +29 -0
- package/src/__tests__/off.test.ts +214 -0
- package/src/__tests__/onany.test.ts +173 -0
- package/src/__tests__/once.test.ts +71 -0
- package/src/__tests__/retain.test.ts +66 -0
- package/src/__tests__/scope.test.ts +111 -0
- package/src/__tests__/waitFor.test.ts +109 -0
- package/src/__tests__/wildcard.test.ts +186 -0
- package/src/event.ts +470 -5
- package/src/index.ts +3 -1
- package/src/scope.ts +88 -0
- package/src/types.ts +57 -0
- package/src/typestest.ts +102 -0
- package/src/utils/isPathMatched.ts +40 -0
- package/src/utils/removeItem.ts +16 -0
- package/tsconfig.json +112 -0
- package/tsup.config.ts +19 -0
package/readme_cn.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# FastEvent
|
|
2
|
+
|
|
3
|
+
FastEvent 是一个功能强大的`TypeScript`事件管理库,提供了灵活的事件订阅和发布机制,支持事件通配符、作用域、异步事件等特性。
|
|
4
|
+
|
|
5
|
+
对比`EventEmitter2`,`FastEvent`具有以下优势:
|
|
6
|
+
|
|
7
|
+
- 在含通配符发布与订阅时,`FastEvent`的性能比`EventEmitter2`高 `1+`倍左右。
|
|
8
|
+
- `FastEvent`包大小为 `6.3xkb`,而`EventEmitter2`为 `43.4kb`。
|
|
9
|
+
- `FastEvent`拥有更丰富的功能。
|
|
10
|
+
|
|
11
|
+
# 安装
|
|
12
|
+
|
|
13
|
+
使用 npm 安装:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install fastevent
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
或使用 yarn:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
yarn add fastevent
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
# 快速入门
|
|
26
|
+
|
|
27
|
+
## 基本使用
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { FastEvent } from 'fastevent';
|
|
31
|
+
|
|
32
|
+
// 创建事件实例
|
|
33
|
+
const events = new FastEvent();
|
|
34
|
+
|
|
35
|
+
// 订阅事件
|
|
36
|
+
events.on('user/login', (user) => {
|
|
37
|
+
console.log('用户登录:', user);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// 发布事件
|
|
41
|
+
events.emit('user/login', { id: 1, name: 'Alice' });
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
# 指南
|
|
45
|
+
|
|
46
|
+
## 事件通配符
|
|
47
|
+
|
|
48
|
+
FastEvent 支持两种通配符:
|
|
49
|
+
- `*`: 匹配单层路径
|
|
50
|
+
- `**`: 匹配多层路径
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const events = new FastEvent();
|
|
54
|
+
|
|
55
|
+
// 匹配 user/*/login
|
|
56
|
+
events.on('user/*/login', (data) => {
|
|
57
|
+
console.log('任何用户类型的登录:', data);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// 匹配 user 下的所有事件
|
|
61
|
+
events.on('user/**', (data) => {
|
|
62
|
+
console.log('所有用户相关事件:', data);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// 触发事件
|
|
66
|
+
events.emit('user/admin/login', { id: 1 }); // 两个处理器都会被调用
|
|
67
|
+
events.emit('user/admin/profile/update', { name: 'New' }); // 只有 ** 处理器会被调用
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 事件作用域
|
|
71
|
+
|
|
72
|
+
作用域允许你在特定的命名空间下处理事件:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const events = new FastEvent();
|
|
76
|
+
|
|
77
|
+
// 创建用户相关的作用域
|
|
78
|
+
const userScope = events.scope('user');
|
|
79
|
+
|
|
80
|
+
// 在作用域中订阅事件
|
|
81
|
+
userScope.on('login', (data) => {
|
|
82
|
+
console.log('用户登录:', data);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// 等同于 events.emit('user/login', data)
|
|
86
|
+
userScope.emit('login', { id: 1 });
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 一次性事件
|
|
90
|
+
|
|
91
|
+
使用 `once` 订阅只触发一次的事件:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const events = new FastEvent();
|
|
95
|
+
|
|
96
|
+
events.once('startup', () => {
|
|
97
|
+
console.log('应用启动');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
events.emit('startup'); // 输出: 应用启动
|
|
101
|
+
events.emit('startup'); // 无输出,监听器已被移除
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 异步事件
|
|
105
|
+
|
|
106
|
+
支持异步事件处理:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const events = new FastEvent();
|
|
110
|
+
|
|
111
|
+
events.on('data/fetch', async () => {
|
|
112
|
+
const response = await fetch('https://api.example.com/data');
|
|
113
|
+
return await response.json();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// 异步发布事件
|
|
117
|
+
const results = await events.emitAsync('data/fetch');
|
|
118
|
+
console.log('所有处理器的结果:', results);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## 事件等待
|
|
122
|
+
|
|
123
|
+
使用 `waitFor` 等待特定事件发生:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const events = new FastEvent();
|
|
127
|
+
|
|
128
|
+
async function waitForLogin() {
|
|
129
|
+
try {
|
|
130
|
+
// 等待登录事件,超时时间 5 秒
|
|
131
|
+
const userData = await events.waitFor('user/login', 5000);
|
|
132
|
+
console.log('用户已登录:', userData);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.log('登录等待超时');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
waitForLogin();
|
|
139
|
+
// 稍后触发登录事件
|
|
140
|
+
events.emit('user/login', { id: 1, name: 'Alice' });
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 保留事件数据
|
|
144
|
+
|
|
145
|
+
保留最后一次事件数据,新的订阅者会立即收到该数据:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const events = new FastEvent();
|
|
149
|
+
|
|
150
|
+
// 发布事件并保留
|
|
151
|
+
events.emit('config/update', { theme: 'dark' }, true);
|
|
152
|
+
|
|
153
|
+
// 之后的订阅者会立即收到保留的数据
|
|
154
|
+
events.on('config/update', (config) => {
|
|
155
|
+
console.log('配置:', config); // 立即输出: 配置: { theme: 'dark' }
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 多级事件
|
|
160
|
+
|
|
161
|
+
支持发布和订阅多级事件。
|
|
162
|
+
|
|
163
|
+
默认使用 `/` 作为事件路径分隔符,你也可以使用自定义的分隔符:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const events = new FastEvent({
|
|
167
|
+
delimiter: '.'
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 全局事件监听
|
|
172
|
+
|
|
173
|
+
使用 `onAny` 监听所有事件:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const events = new FastEvent();
|
|
177
|
+
|
|
178
|
+
events.onAny((data, meta) => {
|
|
179
|
+
console.log(`事件 ${meta.type} 被触发:`, data);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
events.emit('user/login', { id: 1 }); // 输出: 事件 user/login 被触发: { id: 1 }
|
|
183
|
+
events.emit('system/error', 'Connection failed'); // 输出: 事件 system/error 被触发: Connection failed
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 元数据(Meta)
|
|
187
|
+
|
|
188
|
+
元数据是一种为事件提供额外上下文信息的机制。你可以在全局范围内设置元数据,也可以为单个事件添加特定的元数据。
|
|
189
|
+
|
|
190
|
+
### 全局元数据
|
|
191
|
+
|
|
192
|
+
在创建 FastEvent 实例时设置全局元数据:
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const events = new FastEvent({
|
|
196
|
+
meta: {
|
|
197
|
+
version: '1.0',
|
|
198
|
+
environment: 'production'
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
events.on('user/login', (data, meta) => {
|
|
203
|
+
console.log('事件数据:', data);
|
|
204
|
+
console.log('元数据:', meta); // 包含 type、version 和 environment
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 事件特定元数据
|
|
209
|
+
|
|
210
|
+
在发布事件时可以传递额外的元数据,它会与全局元数据合并:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
const events = new FastEvent({
|
|
214
|
+
meta: { app: 'MyApp' }
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// 在发布事件时添加特定的元数据
|
|
218
|
+
events.emit('order/create',
|
|
219
|
+
{ orderId: '123' }, // 事件数据
|
|
220
|
+
false, // 不保留
|
|
221
|
+
{ timestamp: Date.now() } // 事件特定的元数据
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// 监听器接收合并后的元数据
|
|
225
|
+
events.on('order/create', (data, meta) => {
|
|
226
|
+
console.log('订单:', data); // { orderId: '123' }
|
|
227
|
+
console.log('元数据:', meta); // { type: 'order/create', app: 'MyApp', timestamp: ... }
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## 错误处理
|
|
232
|
+
|
|
233
|
+
FastEvent 提供了错误处理机制:
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
const events = new FastEvent({
|
|
237
|
+
ignoreErrors: true, // 默认为 true,不会抛出错误
|
|
238
|
+
onListenerError: (type, error) => {
|
|
239
|
+
console.error(`处理事件 ${type} 时发生错误:`, error);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
events.on('process', () => {
|
|
244
|
+
throw new Error('处理失败');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// 不会抛出错误,而是触发 onListenerError
|
|
248
|
+
events.emit('process');
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## 自定义选项
|
|
252
|
+
|
|
253
|
+
FastEvent 构造函数支持多个选项:
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
const events = new FastEvent({
|
|
257
|
+
// 事件路径分隔符,默认为 '/'
|
|
258
|
+
delimiter: '.',
|
|
259
|
+
// 事件处理器的上下文
|
|
260
|
+
context: null,
|
|
261
|
+
// 元数据,会传递给所有事件处理器
|
|
262
|
+
meta: { ... },
|
|
263
|
+
|
|
264
|
+
// 错误处理
|
|
265
|
+
ignoreErrors: true,
|
|
266
|
+
onListenerError: (type, error) => {
|
|
267
|
+
console.error(`事件错误:`, type, error);
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
// 监听器添加/移除的回调
|
|
271
|
+
onAddListener: (path, listener) => {
|
|
272
|
+
console.log('添加监听器:', path);
|
|
273
|
+
},
|
|
274
|
+
onRemoveListener: (path, listener) => {
|
|
275
|
+
console.log('移除监听器:', path);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
# 性能
|
|
281
|
+
|
|
282
|
+

|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Bench } from 'tinybench';
|
|
2
|
+
import { FastEvent } from '../event';
|
|
3
|
+
import { EventEmitter2 } from 'eventemitter2';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const bench = new Bench({
|
|
7
|
+
time: 1000,
|
|
8
|
+
iterations: 100,
|
|
9
|
+
});
|
|
10
|
+
const fastEmitter = new FastEvent()
|
|
11
|
+
const emitter2 = new EventEmitter2({
|
|
12
|
+
wildcard: true,
|
|
13
|
+
delimiter: '.'
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
bench
|
|
17
|
+
.add('[FastEvent] 多级路径事件的发布与订阅', () => {
|
|
18
|
+
return new Promise<void>((resolve) => {
|
|
19
|
+
const subscriber = fastEmitter.on('a/b/c/d/e/f', () => {
|
|
20
|
+
resolve()
|
|
21
|
+
})
|
|
22
|
+
fastEmitter.emit('a/b/c/d/e/f', 1)
|
|
23
|
+
subscriber.off()
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
.add('[EventEmitter2] 多级路径事件的发布与订阅', () => {
|
|
27
|
+
return new Promise<void>((resolve) => {
|
|
28
|
+
const subscriber = emitter2.on('a.b.c.d.e.f', () => {
|
|
29
|
+
resolve()
|
|
30
|
+
},{objectify:true})
|
|
31
|
+
emitter2.emit('a.b.c.d.e.f', 1)
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
subscriber.off()
|
|
34
|
+
})
|
|
35
|
+
}) ;
|
|
36
|
+
//
|
|
37
|
+
(async () => {
|
|
38
|
+
await bench.run();
|
|
39
|
+
console.table(bench.table());
|
|
40
|
+
})();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Bench } from 'tinybench';
|
|
2
|
+
import { FastEvent } from '../event';
|
|
3
|
+
import { EventEmitter2 } from 'eventemitter2';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const bench = new Bench({
|
|
7
|
+
time: 1000,
|
|
8
|
+
iterations: 100,
|
|
9
|
+
});
|
|
10
|
+
const fastEmitter = new FastEvent()
|
|
11
|
+
const emitter2 = new EventEmitter2({
|
|
12
|
+
wildcard: true,
|
|
13
|
+
delimiter: '.'
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
bench
|
|
17
|
+
.add('[FastEvent] 发布与订阅', () => {
|
|
18
|
+
return new Promise<void>((resolve) => {
|
|
19
|
+
const subscriber= fastEmitter.on('x', () => {
|
|
20
|
+
resolve()
|
|
21
|
+
})
|
|
22
|
+
fastEmitter.emit('x', 1)
|
|
23
|
+
subscriber.off()
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
.add('[EventEmitter2] 发布与订阅', () => {
|
|
27
|
+
return new Promise<void>((resolve) => {
|
|
28
|
+
const subscriber= emitter2.on('x', () => {
|
|
29
|
+
resolve()
|
|
30
|
+
})
|
|
31
|
+
emitter2.emit('x', 1)
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
subscriber.off()
|
|
34
|
+
})
|
|
35
|
+
}) ;
|
|
36
|
+
//
|
|
37
|
+
(async () => {
|
|
38
|
+
await bench.run();
|
|
39
|
+
console.table(bench.table());
|
|
40
|
+
})();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Bench } from 'tinybench';
|
|
2
|
+
import { FastEvent } from '../event';
|
|
3
|
+
import { EventEmitter2 } from 'eventemitter2';
|
|
4
|
+
|
|
5
|
+
const bench = new Bench({
|
|
6
|
+
time: 2000,
|
|
7
|
+
iterations: 200,
|
|
8
|
+
});
|
|
9
|
+
const fastEmitter = new FastEvent()
|
|
10
|
+
const emitter2 = new EventEmitter2({
|
|
11
|
+
wildcard: true,
|
|
12
|
+
delimiter: '.'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
bench
|
|
18
|
+
.add('[FastEvent] 通配符发布与订阅', () => {
|
|
19
|
+
return new Promise<void>((resolve) => {
|
|
20
|
+
const subscriber= fastEmitter.on('a/b/c/*', () => {
|
|
21
|
+
resolve()
|
|
22
|
+
})
|
|
23
|
+
fastEmitter.emit('a/b/c/x', 1)
|
|
24
|
+
subscriber.off()
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
.add('[EventEmitter2] 通配符发布与订阅', () => {
|
|
28
|
+
return new Promise<void>((resolve) => {
|
|
29
|
+
const subscriber = emitter2.on('a.b.c.*', () => {
|
|
30
|
+
resolve()
|
|
31
|
+
},{objectify:true})
|
|
32
|
+
emitter2.emit('a.b.c.x', 1)
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
subscriber.off()
|
|
35
|
+
})
|
|
36
|
+
});
|
|
37
|
+
//
|
|
38
|
+
(async () => {
|
|
39
|
+
await bench.run();
|
|
40
|
+
console.table(bench.table());
|
|
41
|
+
})();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest"
|
|
2
|
+
import { FastEvent } from "../event"
|
|
3
|
+
|
|
4
|
+
describe("简单发布与订阅",async ()=>{
|
|
5
|
+
test("简单发布订阅事件",()=>{
|
|
6
|
+
const emitter = new FastEvent()
|
|
7
|
+
emitter.on("x",(payload,{type})=>{
|
|
8
|
+
expect(type).toBe("x")
|
|
9
|
+
expect(payload).toBe(1)
|
|
10
|
+
})
|
|
11
|
+
emitter.emit("x",1)
|
|
12
|
+
})
|
|
13
|
+
test("简单发布订阅事件后取消",()=>{
|
|
14
|
+
const emitter = new FastEvent()
|
|
15
|
+
const events:string[]=[]
|
|
16
|
+
const subscriber = emitter.on("x",(payload,{type})=>{
|
|
17
|
+
expect(type).toBe("x")
|
|
18
|
+
expect(payload).toBe(1)
|
|
19
|
+
events.push(type)
|
|
20
|
+
})
|
|
21
|
+
emitter.emit("x",1)
|
|
22
|
+
expect(events).toEqual(["x"])
|
|
23
|
+
subscriber.off()
|
|
24
|
+
emitter.emit("x",1)
|
|
25
|
+
emitter.emit("x",1)
|
|
26
|
+
emitter.emit("x",1)
|
|
27
|
+
expect(events).toEqual(["x"])
|
|
28
|
+
})
|
|
29
|
+
test("发布订阅层级事件",()=>{
|
|
30
|
+
const emitter = new FastEvent()
|
|
31
|
+
const events:string[]=[]
|
|
32
|
+
emitter.on("a.b.c",(payload,{type})=>{
|
|
33
|
+
expect(type).toBe("a.b.c")
|
|
34
|
+
expect(payload).toBe(1)
|
|
35
|
+
events.push(type)
|
|
36
|
+
})
|
|
37
|
+
emitter.emit("a",1)
|
|
38
|
+
emitter.emit("a.b",1)
|
|
39
|
+
emitter.emit("a.b.c",1)
|
|
40
|
+
expect(events).toEqual(["a.b.c"])
|
|
41
|
+
})
|
|
42
|
+
test("返回事件执行结果",()=>{
|
|
43
|
+
const emitter = new FastEvent()
|
|
44
|
+
for(let i=1;i<=10;i++){
|
|
45
|
+
emitter.on("x",()=>{
|
|
46
|
+
return i
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
const results = emitter.emit("x",1)
|
|
50
|
+
expect(results).toEqual([1,2,3,4,5,6,7,8,9,10])
|
|
51
|
+
})
|
|
52
|
+
test("侦听器执行出错返回事件执行结果",async ()=>{
|
|
53
|
+
const emitter = new FastEvent()
|
|
54
|
+
for(let i=1;i<=10;i++){
|
|
55
|
+
emitter.on("x",()=>{
|
|
56
|
+
if(i % 2 ==0) throw new Error("custom")
|
|
57
|
+
return i
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
const results = emitter.emit("x",1)
|
|
61
|
+
for(let i=1;i<=10;i++){
|
|
62
|
+
if(i % 2 ==0) expect(results[i-1]).toBeInstanceOf(Error)
|
|
63
|
+
else expect(results[i-1]).toBe(i)
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
test("侦听器执行出错时emit出错",async ()=>{
|
|
67
|
+
const emitter = new FastEvent({ignoreErrors:false})
|
|
68
|
+
const err = new Error("custom")
|
|
69
|
+
for(let i=1;i<=10;i++){
|
|
70
|
+
emitter.on("x",()=>{
|
|
71
|
+
if(i % 2 ==0) throw err
|
|
72
|
+
return i
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
expect(()=>emitter.emit("x",1)).toThrow(err);
|
|
76
|
+
// @ts-ignore, 当执行侦听器出错时会在错误对象上挂载一个_listener属性代表当前执行的侦听器路径
|
|
77
|
+
expect(err._trigger).toBe("x")
|
|
78
|
+
|
|
79
|
+
})
|
|
80
|
+
test("添加侦听器时指定顺序",async ()=>{
|
|
81
|
+
const emitter = new FastEvent()
|
|
82
|
+
const types:number[]=[]
|
|
83
|
+
emitter.on("x",()=>{
|
|
84
|
+
types.push(1)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
emitter.on("x",()=>{
|
|
88
|
+
types.push(2)
|
|
89
|
+
},{prepend:true})
|
|
90
|
+
emitter.emit("x")
|
|
91
|
+
expect(types).toEqual([2,1])
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest"
|
|
2
|
+
import { FastEvent } from "../event"
|
|
3
|
+
|
|
4
|
+
describe("异步发布与订阅",async ()=>{
|
|
5
|
+
test("异步发布订阅事件",async ()=>{
|
|
6
|
+
const emitter = new FastEvent()
|
|
7
|
+
const events: any[] = []
|
|
8
|
+
emitter.on("x",async (payload,{type})=>{
|
|
9
|
+
events.push({type, payload})
|
|
10
|
+
})
|
|
11
|
+
await emitter.emitAsync("x",1)
|
|
12
|
+
expect(events[0].type).toBe("x")
|
|
13
|
+
expect(events[0].payload).toBe(1)
|
|
14
|
+
})
|
|
15
|
+
test("异步发布订阅事件后取消",async ()=>{
|
|
16
|
+
const emitter = new FastEvent()
|
|
17
|
+
const events:string[]=[]
|
|
18
|
+
const subscriber = emitter.on("x",async (payload,{type})=>{
|
|
19
|
+
events.push(type)
|
|
20
|
+
})
|
|
21
|
+
await emitter.emitAsync("x",1)
|
|
22
|
+
expect(events).toEqual(["x"])
|
|
23
|
+
subscriber.off()
|
|
24
|
+
await emitter.emitAsync("x",1)
|
|
25
|
+
await emitter.emitAsync("x",1)
|
|
26
|
+
await emitter.emitAsync("x",1)
|
|
27
|
+
expect(events).toEqual(["x"])
|
|
28
|
+
})
|
|
29
|
+
test("发布订阅层级事件",async ()=>{
|
|
30
|
+
const emitter = new FastEvent()
|
|
31
|
+
const events:string[]=[]
|
|
32
|
+
emitter.on("a.b.c",async (payload,{type})=>{
|
|
33
|
+
events.push(type)
|
|
34
|
+
})
|
|
35
|
+
await emitter.emitAsync("a",1)
|
|
36
|
+
await emitter.emitAsync("a.b",1)
|
|
37
|
+
await emitter.emitAsync("a.b.c",1)
|
|
38
|
+
expect(events).toEqual(["a.b.c"])
|
|
39
|
+
})
|
|
40
|
+
test("返回事件执行结果",async ()=>{
|
|
41
|
+
const emitter = new FastEvent()
|
|
42
|
+
for(let i=1;i<=10;i++){
|
|
43
|
+
emitter.on("x",async ()=>{
|
|
44
|
+
return i
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
const results = await emitter.emitAsync("x",1)
|
|
48
|
+
expect(results).toEqual([1,2,3,4,5,6,7,8,9,10])
|
|
49
|
+
})
|
|
50
|
+
test("异步侦听器执行出错返回事件执行结果",async ()=>{
|
|
51
|
+
const emitter = new FastEvent()
|
|
52
|
+
for(let i=1;i<=10;i++){
|
|
53
|
+
emitter.on("x",async ()=>{
|
|
54
|
+
if(i % 2 ==0) throw new Error("custom")
|
|
55
|
+
return i
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
const results = await emitter.emitAsync("x",1)
|
|
59
|
+
for(let i=1;i<=10;i++){
|
|
60
|
+
if(i % 2 ==0) expect(results[i-1]).toBeInstanceOf(Error)
|
|
61
|
+
else expect(results[i-1]).toBe(i)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|