fastevent 1.0.3 → 1.1.0

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/readme_cn.md CHANGED
@@ -4,9 +4,9 @@ FastEvent 是一个功能强大的`TypeScript`事件管理库,提供了灵活
4
4
 
5
5
  对比`EventEmitter2`,`FastEvent`具有以下优势:
6
6
 
7
- - 在含通配符发布与订阅时,`FastEvent`的性能比`EventEmitter2`高 `1+`倍左右。
8
- - `FastEvent`包大小为 `6.3xkb`,而`EventEmitter2`为 `43.4kb`。
9
- - `FastEvent`拥有更丰富的功能。
7
+ - `FastEvent`的性能比`EventEmitter2`高 `1~2`倍左右。
8
+ - `FastEvent`包大小为 `6.xkb`,而`EventEmitter2`为 `43.4kb`。
9
+ - `FastEvent`拥有更丰富的功能。
10
10
 
11
11
  # 安装
12
12
 
@@ -33,43 +33,67 @@ import { FastEvent } from 'fastevent';
33
33
  const events = new FastEvent();
34
34
 
35
35
  // 订阅事件
36
- events.on('user/login', (user) => {
37
- console.log('用户登录:', user);
36
+ events.on('user/login', (message) => {
37
+ console.log('用户登录:', message.payload);
38
+ console.log('事件类型:', message.type);
39
+ console.log('元数据:', message.meta);
38
40
  });
39
41
 
40
- // 发布事件
42
+ // 发布事件 - 方式1:参数形式
41
43
  events.emit('user/login', { id: 1, name: 'Alice' });
44
+
45
+ // 发布事件 - 方式2:消息对象形式
46
+ events.emit({
47
+ type: 'user/login',
48
+ payload: { id: 1, name: 'Alice' },
49
+ meta: { timestamp: Date.now() },
50
+ });
42
51
  ```
43
52
 
44
53
  # 指南
45
54
 
55
+ ## 事件消息格式
56
+
57
+ FastEvent 使用标准化的消息格式处理所有事件:
58
+
59
+ ```typescript
60
+ type FastEventMessage<T = string, P = any, M = unknown> = {
61
+ type: T; // 事件类型
62
+ payload: P; // 事件数据
63
+ meta: M; // 事件元数据
64
+ };
65
+ ```
66
+
67
+ 事件监听器始终接收这个消息对象,提供了一致的方式来访问事件数据和元数据。
68
+
46
69
  ## 事件通配符
47
70
 
48
71
  FastEvent 支持两种通配符:
49
- - `*`: 匹配单层路径
50
- - `**`: 匹配多层路径
72
+
73
+ - `*`: 匹配单层路径
74
+ - `**`: 匹配多层路径
51
75
 
52
76
  ```typescript
53
77
  const events = new FastEvent();
54
78
 
55
79
  // 匹配 user/*/login
56
- events.on('user/*/login', (data) => {
57
- console.log('任何用户类型的登录:', data);
80
+ events.on('user/*/login', (message) => {
81
+ console.log('任何用户类型的登录:', message.payload);
58
82
  });
59
83
 
60
84
  // 匹配 user 下的所有事件
61
- events.on('user/**', (data) => {
62
- console.log('所有用户相关事件:', data);
85
+ events.on('user/**', (message) => {
86
+ console.log('所有用户相关事件:', message.payload);
63
87
  });
64
88
 
65
89
  // 触发事件
66
- events.emit('user/admin/login', { id: 1 }); // 两个处理器都会被调用
67
- events.emit('user/admin/profile/update', { name: 'New' }); // 只有 ** 处理器会被调用
90
+ events.emit('user/admin/login', { id: 1 }); // 两个处理器都会被调用
91
+ events.emit('user/admin/profile/update', { name: 'New' }); // 只有 ** 处理器会被调用
68
92
  ```
69
93
 
70
94
  ## 事件作用域
71
95
 
72
- 作用域允许你在特定的命名空间下处理事件:
96
+ 作用域允许你在特定的命名空间下处理事件。注意,作用域与父事件发射器共享相同的监听器表:
73
97
 
74
98
  ```typescript
75
99
  const events = new FastEvent();
@@ -77,13 +101,59 @@ const events = new FastEvent();
77
101
  // 创建用户相关的作用域
78
102
  const userScope = events.scope('user');
79
103
 
80
- // 在作用域中订阅事件
81
- userScope.on('login', (data) => {
82
- console.log('用户登录:', data);
83
- });
104
+ // 以下两种方式等效:
105
+ userScope.on('login', handler);
106
+ events.on('user/login', handler);
107
+
108
+ // 以下两种方式也等效:
109
+ userScope.emit('login', data);
110
+ events.emit('user/login', data);
111
+
112
+ // 清除作用域中的所有监听器
113
+ userScope.offAll(); // 等效于 events.offAll('user')
114
+ ```
115
+
116
+ ## 监听器选项
117
+
118
+ 订阅事件时可以指定额外的选项:
119
+
120
+ ```typescript
121
+ interface FastEventListenOptions {
122
+ // 监听器被调用的次数(0表示无限次,1表示一次)
123
+ count?: number;
124
+ // 将监听器添加到监听器数组的开头
125
+ prepend?: boolean;
126
+ }
127
+
128
+ // 示例:监听前3次事件
129
+ events.on('data', handler, { count: 3 });
130
+
131
+ // 示例:确保处理器在其他监听器之前被调用
132
+ events.on('important', handler, { prepend: true });
133
+ ```
134
+
135
+ ## 移除监听器
136
+
137
+ FastEvent 提供多种移除监听器的方式:
138
+
139
+ ```typescript
140
+ // 移除特定监听器
141
+ events.off(listener);
142
+
143
+ // 移除某个事件的所有监听器
144
+ events.off('user/login');
145
+
146
+ // 移除某个事件的特定监听器
147
+ events.off('user/login', listener);
148
+
149
+ // 使用通配符模式移除监听器
150
+ events.off('user/*');
151
+
152
+ // 移除所有监听器
153
+ events.offAll();
84
154
 
85
- // 等同于 events.emit('user/login', data)
86
- userScope.emit('login', { id: 1 });
155
+ // 移除某个前缀下的所有监听器
156
+ events.offAll('user');
87
157
  ```
88
158
 
89
159
  ## 一次性事件
@@ -94,11 +164,11 @@ userScope.emit('login', { id: 1 });
94
164
  const events = new FastEvent();
95
165
 
96
166
  events.once('startup', () => {
97
- console.log('应用启动');
167
+ console.log('应用启动');
98
168
  });
99
169
 
100
- events.emit('startup'); // 输出: 应用启动
101
- events.emit('startup'); // 无输出,监听器已被移除
170
+ // 等效于:
171
+ events.on('startup', handler, { count: 1 });
102
172
  ```
103
173
 
104
174
  ## 异步事件
@@ -109,15 +179,42 @@ events.emit('startup'); // 无输出,监听器已被移除
109
179
  const events = new FastEvent();
110
180
 
111
181
  events.on('data/fetch', async () => {
112
- const response = await fetch('https://api.example.com/data');
113
- return await response.json();
182
+ const response = await fetch('https://api.example.com/data');
183
+ return await response.json();
114
184
  });
115
185
 
116
- // 异步发布事件
186
+ // 异步发布事件返回所有结果/错误的数组
117
187
  const results = await events.emitAsync('data/fetch');
118
188
  console.log('所有处理器的结果:', results);
119
189
  ```
120
190
 
191
+ ## 监听器返回值
192
+
193
+ `emit` 和 `emitAsync` 方法都会返回所有事件监听器的执行结果:
194
+
195
+ ```typescript
196
+ const events = new FastEvent();
197
+
198
+ // 同步监听器的返回值
199
+ events.on('calculate', () => 1);
200
+ events.on('calculate', () => 2);
201
+ events.on('calculate', () => 3);
202
+
203
+ // 获取返回值数组
204
+ const results = events.emit('calculate');
205
+ console.log('结果:', results); // [1, 2, 3]
206
+
207
+ // 异步监听器
208
+ events.on('process', async () => '结果 1');
209
+ events.on('process', async () => '结果 2');
210
+
211
+ // 获取异步结果/错误数组
212
+ const asyncResults = await events.emitAsync('process');
213
+ console.log('异步结果:', asyncResults); // ['结果 1', '结果 2']
214
+ ```
215
+
216
+ 对于异步事件,`emitAsync` 会等待所有监听器完成执行,并返回一个数组,包含所有监听器的解析值,如果监听器执行失败则包含错误对象。
217
+
121
218
  ## 事件等待
122
219
 
123
220
  使用 `waitFor` 等待特定事件发生:
@@ -126,13 +223,13 @@ console.log('所有处理器的结果:', results);
126
223
  const events = new FastEvent();
127
224
 
128
225
  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
- }
226
+ try {
227
+ // 等待登录事件,超时时间 5 秒
228
+ const userData = await events.waitFor('user/login', 5000);
229
+ console.log('用户已登录:', userData);
230
+ } catch (error) {
231
+ console.log('登录等待超时');
232
+ }
136
233
  }
137
234
 
138
235
  waitForLogin();
@@ -151,8 +248,8 @@ const events = new FastEvent();
151
248
  events.emit('config/update', { theme: 'dark' }, true);
152
249
 
153
250
  // 之后的订阅者会立即收到保留的数据
154
- events.on('config/update', (config) => {
155
- console.log('配置:', config); // 立即输出: 配置: { theme: 'dark' }
251
+ events.on('config/update', (message) => {
252
+ console.log('配置:', message.payload); // 立即输出: 配置: { theme: 'dark' }
156
253
  });
157
254
  ```
158
255
 
@@ -164,7 +261,7 @@ events.on('config/update', (config) => {
164
261
 
165
262
  ```typescript
166
263
  const events = new FastEvent({
167
- delimiter: '.'
264
+ delimiter: '.',
168
265
  });
169
266
  ```
170
267
 
@@ -175,12 +272,12 @@ const events = new FastEvent({
175
272
  ```typescript
176
273
  const events = new FastEvent();
177
274
 
178
- events.onAny((data, meta) => {
179
- console.log(`事件 ${meta.type} 被触发:`, data);
275
+ events.onAny((message) => {
276
+ console.log(`事件 ${message.type} 被触发:`, message.payload);
180
277
  });
181
278
 
182
- events.emit('user/login', { id: 1 }); // 输出: 事件 user/login 被触发: { id: 1 }
183
- events.emit('system/error', 'Connection failed'); // 输出: 事件 system/error 被触发: Connection failed
279
+ // 也可以使用 prepend 选项
280
+ events.onAny(handler, { prepend: true });
184
281
  ```
185
282
 
186
283
  ## 元数据(Meta)
@@ -193,15 +290,15 @@ events.emit('system/error', 'Connection failed'); // 输出: 事件 system/erro
193
290
 
194
291
  ```typescript
195
292
  const events = new FastEvent({
196
- meta: {
197
- version: '1.0',
198
- environment: 'production'
199
- }
293
+ meta: {
294
+ version: '1.0',
295
+ environment: 'production',
296
+ },
200
297
  });
201
298
 
202
- events.on('user/login', (data, meta) => {
203
- console.log('事件数据:', data);
204
- console.log('元数据:', meta); // 包含 type、version 和 environment
299
+ events.on('user/login', (message) => {
300
+ console.log('事件数据:', message.payload);
301
+ console.log('元数据:', message.meta); // 包含 type、version 和 environment
205
302
  });
206
303
  ```
207
304
 
@@ -211,20 +308,21 @@ events.on('user/login', (data, meta) => {
211
308
 
212
309
  ```typescript
213
310
  const events = new FastEvent({
214
- meta: { app: 'MyApp' }
311
+ meta: { app: 'MyApp' },
215
312
  });
216
313
 
217
314
  // 在发布事件时添加特定的元数据
218
- events.emit('order/create',
219
- { orderId: '123' }, // 事件数据
220
- false, // 不保留
221
- { timestamp: Date.now() } // 事件特定的元数据
315
+ events.emit(
316
+ 'order/create',
317
+ { orderId: '123' }, // 事件数据
318
+ false, // 不保留
319
+ { timestamp: Date.now() }, // 事件特定的元数据
222
320
  );
223
321
 
224
322
  // 监听器接收合并后的元数据
225
- events.on('order/create', (data, meta) => {
226
- console.log('订单:', data); // { orderId: '123' }
227
- console.log('元数据:', meta); // { type: 'order/create', app: 'MyApp', timestamp: ... }
323
+ events.on('order/create', (message) => {
324
+ console.log('订单:', message.payload); // { orderId: '123' }
325
+ console.log('元数据:', message.meta); // { type: 'order/create', app: 'MyApp', timestamp: ... }
228
326
  });
229
327
  ```
230
328
 
@@ -234,20 +332,47 @@ FastEvent 提供了错误处理机制:
234
332
 
235
333
  ```typescript
236
334
  const events = new FastEvent({
237
- ignoreErrors: true, // 默认为 true,不会抛出错误
238
- onListenerError: (type, error) => {
239
- console.error(`处理事件 ${type} 时发生错误:`, error);
240
- }
335
+ ignoreErrors: true, // 默认为 true,不会抛出错误
336
+ onListenerError: (type, error) => {
337
+ console.error(`处理事件 ${type} 时发生错误:`, error);
338
+ },
241
339
  });
242
340
 
243
341
  events.on('process', () => {
244
- throw new Error('处理失败');
342
+ throw new Error('处理失败');
245
343
  });
246
344
 
247
345
  // 不会抛出错误,而是触发 onListenerError
248
346
  events.emit('process');
249
347
  ```
250
348
 
349
+ ## TypeScript 类型支持
350
+
351
+ FastEvent 使用 TypeScript 编写,提供完整的类型支持:
352
+
353
+ ```typescript
354
+ // 定义事件类型
355
+ interface MyEvents {
356
+ 'user/login': { id: number; name: string };
357
+ 'user/logout': { id: number };
358
+ }
359
+
360
+ // 创建带类型的事件发射器
361
+ const events = new FastEvent<MyEvents>();
362
+
363
+ // 事件名称和数据类型检查
364
+ events.on('user/login', (message) => {
365
+ // message.payload 的类型为 { id: number; name: string }
366
+ const { id, name } = message.payload;
367
+ });
368
+
369
+ // 错误:错误的事件名称
370
+ events.emit('wrong/event', {});
371
+
372
+ // 错误:错误的数据类型
373
+ events.emit('user/login', { wrong: 'type' });
374
+ ```
375
+
251
376
  ## 自定义选项
252
377
 
253
378
  FastEvent 构造函数支持多个选项:
@@ -255,18 +380,18 @@ FastEvent 构造函数支持多个选项:
255
380
  ```typescript
256
381
  const events = new FastEvent({
257
382
  // 事件路径分隔符,默认为 '/'
258
- delimiter: '.',
383
+ delimiter: '.',
259
384
  // 事件处理器的上下文
260
- context: null,
385
+ context: null,
261
386
  // 元数据,会传递给所有事件处理器
262
- meta: { ... },
263
-
387
+ meta: { ... },
388
+
264
389
  // 错误处理
265
390
  ignoreErrors: true,
266
391
  onListenerError: (type, error) => {
267
392
  console.error(`事件错误:`, type, error);
268
393
  },
269
-
394
+
270
395
  // 监听器添加/移除的回调
271
396
  onAddListener: (path, listener) => {
272
397
  console.log('添加监听器:', path);
@@ -279,4 +404,4 @@ const events = new FastEvent({
279
404
 
280
405
  # 性能
281
406
 
282
- ![](./bench.png)
407
+ ![](./bench.png)
@@ -1,94 +1,106 @@
1
1
  import { describe, test, expect } from "vitest"
2
- import { FastEvent } from "../event"
2
+ import { FastEvent } from "../event"
3
3
 
4
- describe("简单发布与订阅",async ()=>{
5
- test("简单发布订阅事件",()=>{
6
- const emitter = new FastEvent()
7
- emitter.on("x",(payload,{type})=>{
4
+ describe("简单发布与订阅", async () => {
5
+ test("简单发布订阅事件", () => {
6
+ const emitter = new FastEvent()
7
+ emitter.on("x", ({ type, payload }) => {
8
8
  expect(type).toBe("x")
9
- expect(payload).toBe(1)
9
+ expect(payload).toBe(1)
10
10
  })
11
- emitter.emit("x",1)
11
+ emitter.emit("x", 1)
12
12
  })
13
- test("简单发布订阅事件后取消",()=>{
14
- const emitter = new FastEvent()
15
- const events:string[]=[]
16
- const subscriber = emitter.on("x",(payload,{type})=>{
13
+ test("简单发布订阅事件后取消", () => {
14
+ const emitter = new FastEvent()
15
+ const events: string[] = []
16
+ const subscriber = emitter.on("x", ({ type, payload }) => {
17
17
  expect(type).toBe("x")
18
- expect(payload).toBe(1)
19
- events.push(type)
18
+ expect(payload).toBe(1)
19
+ events.push(type)
20
20
  })
21
- emitter.emit("x",1)
21
+ emitter.emit("x", 1)
22
22
  expect(events).toEqual(["x"])
23
23
  subscriber.off()
24
- emitter.emit("x",1)
25
- emitter.emit("x",1)
26
- emitter.emit("x",1)
24
+ emitter.emit("x", 1)
25
+ emitter.emit("x", 1)
26
+ emitter.emit("x", 1)
27
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})=>{
28
+ })
29
+ test("发布订阅层级事件", () => {
30
+ const emitter = new FastEvent()
31
+ const events: string[] = []
32
+ emitter.on("a.b.c", ({ type, payload }) => {
33
33
  expect(type).toBe("a.b.c")
34
- expect(payload).toBe(1)
35
- events.push(type)
34
+ expect(payload).toBe(1)
35
+ events.push(type)
36
36
  })
37
- emitter.emit("a",1)
38
- emitter.emit("a.b",1)
39
- emitter.emit("a.b.c",1)
37
+ emitter.emit("a", 1)
38
+ emitter.emit("a.b", 1)
39
+ emitter.emit("a.b.c", 1)
40
40
  expect(events).toEqual(["a.b.c"])
41
41
  })
42
- test("返回事件执行结果",()=>{
43
- const emitter = new FastEvent()
44
- for(let i=1;i<=10;i++){
45
- emitter.on("x",()=>{
42
+ test("返回事件执行结果", () => {
43
+ const emitter = new FastEvent()
44
+ for (let i = 1; i <= 10; i++) {
45
+ emitter.on("x", () => {
46
46
  return i
47
47
  })
48
48
  }
49
- const results = emitter.emit("x",1)
50
- expect(results).toEqual([1,2,3,4,5,6,7,8,9,10])
49
+ const results = emitter.emit("x", 1)
50
+ expect(results).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
51
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")
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
57
  return i
58
58
  })
59
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)
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
64
  }
65
65
  })
66
- test("侦听器执行出错时emit出错",async ()=>{
67
- const emitter = new FastEvent({ignoreErrors:false})
66
+ test("侦听器执行出错时emit出错", async () => {
67
+ const emitter = new FastEvent({ ignoreErrors: false })
68
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
69
+ for (let i = 1; i <= 10; i++) {
70
+ emitter.on("x", () => {
71
+ if (i % 2 == 0) throw err
72
72
  return i
73
73
  })
74
74
  }
75
- expect(()=>emitter.emit("x",1)).toThrow(err);
75
+ expect(() => emitter.emit("x", 1)).toThrow(err);
76
76
  // @ts-ignore, 当执行侦听器出错时会在错误对象上挂载一个_listener属性代表当前执行的侦听器路径
77
- expect(err._trigger).toBe("x")
78
-
77
+ expect(err._emitter).toBe("x")
78
+
79
79
  })
80
- test("添加侦听器时指定顺序",async ()=>{
81
- const emitter = new FastEvent()
82
- const types:number[]=[]
83
- emitter.on("x",()=>{
80
+ test("添加侦听器时指定顺序", async () => {
81
+ const emitter = new FastEvent()
82
+ const types: number[] = []
83
+ emitter.on("x", () => {
84
84
  types.push(1)
85
85
  })
86
-
87
- emitter.on("x",()=>{
86
+
87
+ emitter.on("x", () => {
88
88
  types.push(2)
89
- },{prepend:true})
89
+ }, { prepend: true })
90
90
  emitter.emit("x")
91
- expect(types).toEqual([2,1])
92
- })
93
- })
94
-
91
+ expect(types).toEqual([2, 1])
92
+ })
93
+ test("简单发布订阅message事件", () => {
94
+ const emitter = new FastEvent()
95
+ emitter.on("x", ({ type, payload, meta }) => {
96
+ expect(type).toBe("x")
97
+ expect(meta.a).toBe(1)
98
+ expect(payload).toBe(1)
99
+ })
100
+ emitter.emit({
101
+ type: "x",
102
+ payload: 1,
103
+ meta: { a: 1 }
104
+ })
105
+ })
106
+ })