mm_expand 1.9.3 → 1.9.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/README.md +66 -2
- package/README_EN.md +102 -0
- package/lib/base.js +111 -20
- package/lib/event.js +3 -3
- package/lib/eventer.js +194 -34
- package/lib/log.js +12 -12
- package/package.json +3 -3
- package/test/array_test.js +0 -167
- package/test/base_test.js +0 -87
- package/test/date_test.js +0 -124
- package/test/event_test.js +0 -112
- package/test/eventer_test.js +0 -395
- package/test/file_test.js +0 -104
- package/test/global_test.js +0 -115
- package/test/index.js +0 -61
- package/test/lang_test.js +0 -118
- package/test/log_test.js +0 -89
- package/test/number_test.js +0 -140
- package/test/object_test.js +0 -106
- package/test/string_test.js +0 -141
- package/test/test.txt +0 -1
- package/test/timer_test.js +0 -103
package/README.md
CHANGED
|
@@ -416,6 +416,69 @@ await $.sleep(2000, obj, 'ok');
|
|
|
416
416
|
- 参数:同$.eventer.runParallel
|
|
417
417
|
- 返回值:包含执行结果和取消方法的对象
|
|
418
418
|
|
|
419
|
+
##### 异步事件触发
|
|
420
|
+
|
|
421
|
+
##### $.eventer.emitWait(eventName, ...args)
|
|
422
|
+
- 描述:异步触发事件,按顺序执行等待完成
|
|
423
|
+
- 参数:
|
|
424
|
+
- eventName: 事件名称(支持命名空间格式:namespace:event)
|
|
425
|
+
- ...args: 传递给事件处理器的参数
|
|
426
|
+
- 返回值:Promise,按优先级顺序执行所有监听器
|
|
427
|
+
- 示例:
|
|
428
|
+
```javascript
|
|
429
|
+
// 顺序执行用户登录事件的所有监听器
|
|
430
|
+
await $.eventer.emitWait('user:login', { id: 1, name: '张三' });
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
##### $.eventer.emitAsync(eventName, ...args)
|
|
434
|
+
- 描述:异步触发事件,并发执行不等待
|
|
435
|
+
- 参数:
|
|
436
|
+
- eventName: 事件名称(支持命名空间格式:namespace:event)
|
|
437
|
+
- ...args: 传递给事件处理器的参数
|
|
438
|
+
- 返回值:布尔值,表示是否有监听器被触发
|
|
439
|
+
- 示例:
|
|
440
|
+
```javascript
|
|
441
|
+
// 异步触发数据更新事件,不等待执行结果
|
|
442
|
+
$.eventer.emitAsync('data:update', { timestamp: Date.now() });
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
##### $.eventer.emitAll(eventName, ...args)
|
|
446
|
+
- 描述:异步触发事件,并发执行等待完成
|
|
447
|
+
- 参数:
|
|
448
|
+
- eventName: 事件名称(支持命名空间格式:namespace:event)
|
|
449
|
+
- ...args: 传递给事件处理器的参数
|
|
450
|
+
- 返回值:Promise<Array>,包含所有监听器的执行结果
|
|
451
|
+
- 示例:
|
|
452
|
+
```javascript
|
|
453
|
+
// 并发执行所有数据处理监听器,等待全部完成
|
|
454
|
+
const results = await $.eventer.emitAll('data:process', { items: [...] });
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
##### $.eventer.emitRace(eventName, ...args)
|
|
458
|
+
- 描述:异步触发事件,竞争执行(第一个完成即返回)
|
|
459
|
+
- 参数:
|
|
460
|
+
- eventName: 事件名称(支持命名空间格式:namespace:event)
|
|
461
|
+
- ...args: 传递给事件处理器的参数
|
|
462
|
+
- 返回值:Promise,第一个完成的监听器结果
|
|
463
|
+
- 示例:
|
|
464
|
+
```javascript
|
|
465
|
+
// 竞争执行多个缓存策略,使用第一个返回的结果
|
|
466
|
+
const fastestResult = await $.eventer.emitRace('cache:get', { key: 'user_data' });
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
##### $.eventer.emitWaterfall(eventName, initialValue, ...args)
|
|
470
|
+
- 描述:异步触发事件,瀑布流执行(前一个结果传递给下一个)
|
|
471
|
+
- 参数:
|
|
472
|
+
- eventName: 事件名称(支持命名空间格式:namespace:event)
|
|
473
|
+
- initialValue: 初始值
|
|
474
|
+
- ...args: 传递给事件处理器的参数
|
|
475
|
+
- 返回值:Promise,最后一个监听器的执行结果
|
|
476
|
+
- 示例:
|
|
477
|
+
```javascript
|
|
478
|
+
// 瀑布流处理数据,每个处理器接收前一个的结果
|
|
479
|
+
const finalResult = await $.eventer.emitWaterfall('data:pipeline', initialData, { stage: 'processing' });
|
|
480
|
+
```
|
|
481
|
+
|
|
419
482
|
##### 事件控制
|
|
420
483
|
|
|
421
484
|
##### $.eventer.pause(key)
|
|
@@ -561,16 +624,17 @@ A: 目前提供基本的JavaScript支持,TypeScript类型定义正在规划中
|
|
|
561
624
|
## npm发布信息
|
|
562
625
|
|
|
563
626
|
### 发布状态
|
|
564
|
-
- **当前版本**: 1.9.
|
|
627
|
+
- **当前版本**: 1.9.3
|
|
565
628
|
- **npm包名**: mm_expand
|
|
566
629
|
- **发布状态**: 已准备发布
|
|
567
630
|
|
|
568
631
|
### 发布前检查清单
|
|
569
|
-
- ✅ 所有测试通过(
|
|
632
|
+
- ✅ 所有测试通过(87/87,100%通过率)
|
|
570
633
|
- ✅ 文档已更新
|
|
571
634
|
- ✅ 依赖项已正确配置
|
|
572
635
|
- ✅ 包信息完整
|
|
573
636
|
- ✅ 许可证文件存在
|
|
637
|
+
- ✅ 事件系统性能优化完成
|
|
574
638
|
|
|
575
639
|
### 发布命令
|
|
576
640
|
```bash
|
package/README_EN.md
CHANGED
|
@@ -20,6 +20,32 @@ This library extends JavaScript native object prototypes with comprehensive data
|
|
|
20
20
|
- **Data Conversion Tools**: JSON, XML, URL parameters, and other format conversion functions
|
|
21
21
|
- **Lightweight and Efficient**: Core functions are lightweight with good performance optimization
|
|
22
22
|
- **Cross-Platform Compatibility**: Supports Node.js and modern browser environments
|
|
23
|
+
- **Sanguosha Card System**: Complete card game system with card management, character system, and battle mechanics
|
|
24
|
+
|
|
25
|
+
### Key Features
|
|
26
|
+
|
|
27
|
+
#### String Enhancement
|
|
28
|
+
- **$()** - String prototype function enhancement
|
|
29
|
+
- **Chinese Pinyin Processing** - Support Chinese to Pinyin, Pinyin initials
|
|
30
|
+
- **XML/JSON Conversion** - Support XML and JSON mutual conversion
|
|
31
|
+
- **String Formatting** - Support various formatting operations
|
|
32
|
+
|
|
33
|
+
#### Array Enhancement
|
|
34
|
+
- **Array Operations** - Support array CRUD, sorting, filtering, etc.
|
|
35
|
+
- **Array Conversion** - Support array to string, object conversion
|
|
36
|
+
|
|
37
|
+
#### Object Enhancement
|
|
38
|
+
- **Object Operations** - Support deep clone, merge, traversal, etc.
|
|
39
|
+
- **Object Validation** - Support object property validation and type checking
|
|
40
|
+
|
|
41
|
+
#### Date Enhancement
|
|
42
|
+
- **Date Formatting** - Support multiple date format outputs
|
|
43
|
+
- **Date Calculation** - Support date addition/subtraction, comparison
|
|
44
|
+
|
|
45
|
+
#### Event System
|
|
46
|
+
- **Event Listening** - Support asynchronous event listening and triggering
|
|
47
|
+
- **Event Priority** - Support event execution priority setting
|
|
48
|
+
- **Event Cancellation** - Support event execution cancellation mechanism
|
|
23
49
|
|
|
24
50
|
## Installation
|
|
25
51
|
|
|
@@ -416,6 +442,69 @@ await $.sleep(2000, obj, 'ok');
|
|
|
416
442
|
- Parameters: Same as $.eventer.runParallel
|
|
417
443
|
- Returns: Object containing execution results and cancel method
|
|
418
444
|
|
|
445
|
+
##### Asynchronous Event Triggering
|
|
446
|
+
|
|
447
|
+
##### $.eventer.emitWait(eventName, ...args)
|
|
448
|
+
- Description: Asynchronously trigger events, execute sequentially and wait for completion
|
|
449
|
+
- Parameters:
|
|
450
|
+
- eventName: Event name (supports namespace format: namespace:event)
|
|
451
|
+
- ...args: Arguments passed to event handlers
|
|
452
|
+
- Returns: Promise, executes all listeners in priority order
|
|
453
|
+
- Example:
|
|
454
|
+
```javascript
|
|
455
|
+
// Execute all user login event listeners sequentially
|
|
456
|
+
await $.eventer.emitWait('user:login', { id: 1, name: 'John' });
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
##### $.eventer.emitAsync(eventName, ...args)
|
|
460
|
+
- Description: Asynchronously trigger events, execute concurrently without waiting
|
|
461
|
+
- Parameters:
|
|
462
|
+
- eventName: Event name (supports namespace format: namespace:event)
|
|
463
|
+
- ...args: Arguments passed to event handlers
|
|
464
|
+
- Returns: Boolean, indicates whether any listeners were triggered
|
|
465
|
+
- Example:
|
|
466
|
+
```javascript
|
|
467
|
+
// Asynchronously trigger data update event without waiting for results
|
|
468
|
+
$.eventer.emitAsync('data:update', { timestamp: Date.now() });
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
##### $.eventer.emitAll(eventName, ...args)
|
|
472
|
+
- Description: Asynchronously trigger events, execute concurrently and wait for completion
|
|
473
|
+
- Parameters:
|
|
474
|
+
- eventName: Event name (supports namespace format: namespace:event)
|
|
475
|
+
- ...args: Arguments passed to event handlers
|
|
476
|
+
- Returns: Promise<Array>, contains execution results of all listeners
|
|
477
|
+
- Example:
|
|
478
|
+
```javascript
|
|
479
|
+
// Execute all data processing listeners concurrently and wait for all to complete
|
|
480
|
+
const results = await $.eventer.emitAll('data:process', { items: [...] });
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
##### $.eventer.emitRace(eventName, ...args)
|
|
484
|
+
- Description: Asynchronously trigger events, race execution (returns first completed result)
|
|
485
|
+
- Parameters:
|
|
486
|
+
- eventName: Event name (supports namespace format: namespace:event)
|
|
487
|
+
- ...args: Arguments passed to event handlers
|
|
488
|
+
- Returns: Promise, result of the first completed listener
|
|
489
|
+
- Example:
|
|
490
|
+
```javascript
|
|
491
|
+
// Race execution of multiple cache strategies, use the first returned result
|
|
492
|
+
const fastestResult = await $.eventer.emitRace('cache:get', { key: 'user_data' });
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
##### $.eventer.emitWaterfall(eventName, initialValue, ...args)
|
|
496
|
+
- Description: Asynchronously trigger events, waterfall execution (pass result to next)
|
|
497
|
+
- Parameters:
|
|
498
|
+
- eventName: Event name (supports namespace format: namespace:event)
|
|
499
|
+
- initialValue: Initial value
|
|
500
|
+
- ...args: Arguments passed to event handlers
|
|
501
|
+
- Returns: Promise, result of the last listener
|
|
502
|
+
- Example:
|
|
503
|
+
```javascript
|
|
504
|
+
// Waterfall data processing, each handler receives the previous result
|
|
505
|
+
const finalResult = await $.eventer.emitWaterfall('data:pipeline', initialData, { stage: 'processing' });
|
|
506
|
+
```
|
|
507
|
+
|
|
419
508
|
##### Event Control
|
|
420
509
|
|
|
421
510
|
##### $.eventer.pause(key)
|
|
@@ -540,6 +629,19 @@ Welcome to submit Issues and Pull Requests! Please ensure your code follows thes
|
|
|
540
629
|
3. Write test cases to ensure functionality
|
|
541
630
|
4. Run tests before submitting to ensure no new issues are introduced
|
|
542
631
|
|
|
632
|
+
## Release Status
|
|
633
|
+
- **Current Version**: 1.9.3
|
|
634
|
+
- **npm Package Name**: mm_expand
|
|
635
|
+
- **Release Status**: Ready for release
|
|
636
|
+
|
|
637
|
+
### Pre-release Checklist
|
|
638
|
+
- ✅ All tests passed (87/87, 100% pass rate)
|
|
639
|
+
- ✅ Documentation updated
|
|
640
|
+
- ✅ Dependencies correctly configured
|
|
641
|
+
- ✅ Package information complete
|
|
642
|
+
- ✅ License file exists
|
|
643
|
+
- ✅ Event system performance optimization completed
|
|
644
|
+
|
|
543
645
|
## Changelog
|
|
544
646
|
|
|
545
647
|
Detailed changelog can be found on the project's [Release page](https://gitee.com/qiuwenwu91/mm_expand/releases).
|
package/lib/base.js
CHANGED
|
@@ -62,37 +62,37 @@ Base.prototype.logger = function (level, message, ...args) {
|
|
|
62
62
|
* 初始化监听
|
|
63
63
|
*/
|
|
64
64
|
Base.prototype._initListen = function () {
|
|
65
|
-
this.on('init
|
|
65
|
+
this.on('init:before', (ctx) => {
|
|
66
66
|
this.status = 'initing';
|
|
67
67
|
this._initCore(...ctx.params);
|
|
68
68
|
});
|
|
69
|
-
this.on('start
|
|
69
|
+
this.on('start:before', (ctx) => {
|
|
70
70
|
this.status = 'starting';
|
|
71
71
|
});
|
|
72
|
-
this.on('start
|
|
72
|
+
this.on('start:after', (ctx) => {
|
|
73
73
|
this.status = 'running';
|
|
74
74
|
});
|
|
75
|
-
this.on('stop
|
|
75
|
+
this.on('stop:before', (ctx) => {
|
|
76
76
|
this.status = 'stopping';
|
|
77
77
|
});
|
|
78
|
-
this.on('stop
|
|
78
|
+
this.on('stop:after', (ctx) => {
|
|
79
79
|
this.status = 'stopped';
|
|
80
80
|
});
|
|
81
|
-
this.on('restart
|
|
81
|
+
this.on('restart:before', (ctx) => {
|
|
82
82
|
this.status = 'restarting';
|
|
83
83
|
});
|
|
84
|
-
this.on('restart
|
|
84
|
+
this.on('restart:after', (ctx) => {
|
|
85
85
|
this.status = 'running';
|
|
86
86
|
});
|
|
87
|
-
this.on('destroy
|
|
87
|
+
this.on('destroy:before', (ctx) => {
|
|
88
88
|
this.status = 'destroying';
|
|
89
89
|
});
|
|
90
|
-
this.on('destroy
|
|
90
|
+
this.on('destroy:check', (ctx) => {
|
|
91
91
|
if (this.status !== 'stopped') {
|
|
92
92
|
ctx.error = new Error('销毁前必须先停止');
|
|
93
93
|
}
|
|
94
94
|
});
|
|
95
|
-
this.on('destroy
|
|
95
|
+
this.on('destroy:after', (ctx) => {
|
|
96
96
|
this.status = 'destroyed';
|
|
97
97
|
});
|
|
98
98
|
};
|
|
@@ -185,28 +185,119 @@ Base.prototype.do = async function (method, ...args) {
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
let ctx = {
|
|
188
|
-
method
|
|
188
|
+
method,
|
|
189
189
|
params: args,
|
|
190
190
|
result: undefined,
|
|
191
191
|
error: undefined
|
|
192
192
|
};
|
|
193
|
-
|
|
193
|
+
|
|
194
|
+
// 1. 前置处理(顺序执行)
|
|
195
|
+
await this.emitWait(method + ':before', ctx);
|
|
196
|
+
|
|
194
197
|
try {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
// 2. 检查阶段(竞争执行)- 任何一个检查失败就停止
|
|
199
|
+
const checkResult = await this.emitRace(method + ':check', ctx);
|
|
200
|
+
|
|
201
|
+
// 处理检查结果
|
|
202
|
+
if (checkResult) {
|
|
203
|
+
if (checkResult.status === 'rejected') {
|
|
204
|
+
// 检查阶段有错误,直接返回
|
|
205
|
+
ctx.error = checkResult.error;
|
|
206
|
+
return ctx.result;
|
|
207
|
+
} else if (checkResult.status === 'fulfilled' && checkResult.result) {
|
|
208
|
+
// 检查阶段返回了有效结果,直接使用
|
|
209
|
+
ctx.result = checkResult.result;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 3. 执行主方法(如果没有被检查阶段拦截)
|
|
214
|
+
if (!ctx.result) {
|
|
215
|
+
ctx.result = await this[method](...ctx.params);
|
|
200
216
|
}
|
|
201
|
-
|
|
217
|
+
|
|
218
|
+
// 4. 渲染阶段(瀑布式执行)- 适合数据处理管道
|
|
202
219
|
if (ctx.result) {
|
|
203
|
-
this.
|
|
220
|
+
let result = await this.emitWaterfall(method + ':render', ctx.result, ctx);
|
|
221
|
+
if (result) {
|
|
222
|
+
ctx.result = result;
|
|
223
|
+
}
|
|
204
224
|
}
|
|
205
225
|
} catch (error) {
|
|
206
226
|
ctx.error = error;
|
|
207
|
-
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 5. 错误处理(异步执行,不阻塞)
|
|
230
|
+
if (ctx.error) {
|
|
231
|
+
await this.emitAsync(method + ':error', ctx);
|
|
208
232
|
this.logger('error', `[${this.constructor.name}] 执行方法${method}出错:`, ctx.error);
|
|
209
233
|
}
|
|
234
|
+
|
|
235
|
+
// 6. 后置处理(顺序执行)
|
|
236
|
+
await this.emitWait(method + ':after', ctx);
|
|
237
|
+
|
|
238
|
+
return ctx.result;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 快速执行方法
|
|
243
|
+
* @param {String} method 方法名称
|
|
244
|
+
* @param {...Object} args 参数列表
|
|
245
|
+
* @return {Object} 返回执行结果
|
|
246
|
+
*/
|
|
247
|
+
Base.prototype.doEasy = async function (method, ...args) {
|
|
248
|
+
// 快速检查方法是否存在
|
|
249
|
+
if (!this[method]) {
|
|
250
|
+
throw new Error(`[${this.constructor.name}] 方法${method}不存在`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
// 直接执行主方法,跳过所有事件处理
|
|
255
|
+
return await this[method](...args);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
// 简单错误处理
|
|
258
|
+
this.logger('error', `[${this.constructor.name}] 快速执行方法${method}出错:`, error);
|
|
259
|
+
throw error;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 智能执行方法(自动选择do或doEasy)
|
|
265
|
+
* @param {String} method 方法名称
|
|
266
|
+
* @param {...Object} args 参数列表
|
|
267
|
+
* @param {Object} options 执行选项
|
|
268
|
+
* @return {Object} 返回执行结果
|
|
269
|
+
*/
|
|
270
|
+
Base.prototype.doSmart = async function (method, ...args) {
|
|
271
|
+
// 提取选项参数
|
|
272
|
+
let options = {};
|
|
273
|
+
if (args.length > 0 && typeof args[args.length - 1] === 'object') {
|
|
274
|
+
options = args.pop();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 检查是否有监听器
|
|
278
|
+
const hasListeners = this._hasEventListeners(method);
|
|
279
|
+
|
|
280
|
+
// 根据选项和监听器情况选择执行方式
|
|
281
|
+
if (options.fast || !hasListeners) {
|
|
282
|
+
// 快速执行:无监听器或明确要求快速
|
|
283
|
+
return await this.doEasy(method, ...args);
|
|
284
|
+
} else {
|
|
285
|
+
// 完整事件驱动执行
|
|
286
|
+
return await this.do(method, ...args);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 检查方法是否有事件监听器
|
|
292
|
+
* @param {String} method 方法名称
|
|
293
|
+
* @return {Boolean} 是否有监听器
|
|
294
|
+
* @private
|
|
295
|
+
*/
|
|
296
|
+
Base.prototype._hasEventListeners = function (method) {
|
|
297
|
+
return this.listenerCount(method + ':before') > 0 ||
|
|
298
|
+
this.listenerCount(method + ':check') > 0 ||
|
|
299
|
+
this.listenerCount(method + ':render') > 0 ||
|
|
300
|
+
this.listenerCount(method + ':after') > 0;
|
|
210
301
|
}
|
|
211
302
|
|
|
212
303
|
/**
|
package/lib/event.js
CHANGED
|
@@ -30,7 +30,7 @@ Event.prototype.logger = function (level, message, ...args) {
|
|
|
30
30
|
Event.prototype.emitWait = async function (event, ...args) {
|
|
31
31
|
const listeners = this.listeners(event);
|
|
32
32
|
if (listeners.length === 0) {
|
|
33
|
-
|
|
33
|
+
return;
|
|
34
34
|
} else {
|
|
35
35
|
for (const listener of listeners) {
|
|
36
36
|
try {
|
|
@@ -63,12 +63,12 @@ Event.prototype.emitAsync = async function (event, ...args) {
|
|
|
63
63
|
.catch(error => {
|
|
64
64
|
// 错误处理 - 记录但不抛出
|
|
65
65
|
this.logger('error', `Async listener error for event ${event}:`, error);
|
|
66
|
-
this.emit('async
|
|
66
|
+
this.emit('async:error', error, event, args);
|
|
67
67
|
});
|
|
68
68
|
} catch (syncError) {
|
|
69
69
|
// 同步错误处理
|
|
70
70
|
this.logger('error', `Sync listener error for event ${event}:`, syncError);
|
|
71
|
-
this.emit('sync
|
|
71
|
+
this.emit('sync:error', syncError, event, args);
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
|
package/lib/eventer.js
CHANGED
|
@@ -408,13 +408,173 @@ Eventer.prototype.emit = function (key, ...params) {
|
|
|
408
408
|
return { ...result, cancel: () => result.then(r => r && typeof r.cancel === 'function' && r.cancel()) };
|
|
409
409
|
};
|
|
410
410
|
|
|
411
|
+
/**
|
|
412
|
+
* 异步触发事件,按顺序执行等待完成
|
|
413
|
+
* @param {String} key 事件关键字,支持namespace:key格式
|
|
414
|
+
* @param {...any} args 事件参数
|
|
415
|
+
* @returns {Promise} - 事件触发结果
|
|
416
|
+
*/
|
|
417
|
+
Eventer.prototype.emitWait = async function (key, ...args) {
|
|
418
|
+
// 解析命名空间
|
|
419
|
+
var { namespace, key } = this._parseKey(key);
|
|
420
|
+
var fullkey = `${namespace}:${key}`;
|
|
421
|
+
|
|
422
|
+
// 获取事件列表
|
|
423
|
+
var list = this._dict[namespace] && this._dict[namespace][key];
|
|
424
|
+
|
|
425
|
+
if (!list || list.length === 0) {
|
|
426
|
+
throw new Error(`事件 ${fullkey} 没有监听者`);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// 按优先级排序后顺序执行
|
|
430
|
+
var sortedList = [...list].sort((a, b) => a.priority - b.priority);
|
|
431
|
+
|
|
432
|
+
for (const handler of sortedList) {
|
|
433
|
+
try {
|
|
434
|
+
await handler.func(...args);
|
|
435
|
+
} catch (err) {
|
|
436
|
+
this.logger('error', `事件监听器执行失败 [${fullkey}]:`, err);
|
|
437
|
+
throw err;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* 异步触发事件,并发执行不等待
|
|
444
|
+
* @param {String} key 事件关键字,支持namespace:key格式
|
|
445
|
+
* @param {...any} args 事件参数
|
|
446
|
+
* @returns {Boolean} - 是否有监听者
|
|
447
|
+
*/
|
|
448
|
+
Eventer.prototype.emitAsync = async function (key, ...args) {
|
|
449
|
+
// 解析命名空间
|
|
450
|
+
var { namespace, key } = this._parseKey(key);
|
|
451
|
+
var fullkey = `${namespace}:${key}`;
|
|
452
|
+
|
|
453
|
+
// 获取事件列表
|
|
454
|
+
var list = this._dict[namespace] && this._dict[namespace][key];
|
|
455
|
+
|
|
456
|
+
if (!list || list.length === 0) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// 立即启动所有监听器,但不等待
|
|
461
|
+
list.forEach(handler => {
|
|
462
|
+
try {
|
|
463
|
+
// 使用 Promise.resolve 确保异步执行
|
|
464
|
+
Promise.resolve(handler.func(...args))
|
|
465
|
+
.catch(error => {
|
|
466
|
+
// 错误处理 - 记录但不抛出
|
|
467
|
+
this.logger('error', `异步监听器错误 [${fullkey}]:`, error);
|
|
468
|
+
this.emit('async:error', error, fullkey, args);
|
|
469
|
+
});
|
|
470
|
+
} catch (syncError) {
|
|
471
|
+
// 同步错误处理
|
|
472
|
+
this.logger('error', `同步监听器错误 [${fullkey}]:`, syncError);
|
|
473
|
+
this.emit('sync:error', syncError, fullkey, args);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
return true;
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* 异步触发事件,并发执行等待完成
|
|
482
|
+
* @param {String} key 事件关键字,支持namespace:key格式
|
|
483
|
+
* @param {...any} args 事件参数
|
|
484
|
+
* @returns {Promise<Array>} - 所有监听器的执行结果
|
|
485
|
+
*/
|
|
486
|
+
Eventer.prototype.emitAll = async function (key, ...args) {
|
|
487
|
+
// 解析命名空间
|
|
488
|
+
var { namespace, key } = this._parseKey(key);
|
|
489
|
+
|
|
490
|
+
// 获取事件列表
|
|
491
|
+
var list = this._dict[namespace] && this._dict[namespace][key];
|
|
492
|
+
|
|
493
|
+
if (!list || list.length === 0) {
|
|
494
|
+
return [];
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const promises = list.map(handler => {
|
|
498
|
+
try {
|
|
499
|
+
return Promise.resolve(handler.func(...args));
|
|
500
|
+
} catch (error) {
|
|
501
|
+
return Promise.reject(error);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
return await Promise.all(promises);
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* 异步触发事件,竞争执行
|
|
510
|
+
* @param {String} key 事件关键字,支持namespace:key格式
|
|
511
|
+
* @param {...any} args 事件参数
|
|
512
|
+
* @returns {Promise} - 第一个完成的结果
|
|
513
|
+
*/
|
|
514
|
+
Eventer.prototype.emitRace = async function (key, ...args) {
|
|
515
|
+
// 解析命名空间
|
|
516
|
+
var { namespace, key } = this._parseKey(key);
|
|
517
|
+
|
|
518
|
+
// 获取事件列表
|
|
519
|
+
var list = this._dict[namespace] && this._dict[namespace][key];
|
|
520
|
+
|
|
521
|
+
if (!list || list.length === 0) {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const promises = list.map((handler, index) =>
|
|
526
|
+
Promise.resolve(handler.func(...args))
|
|
527
|
+
.then(result => ({ index, result, status: 'fulfilled' }))
|
|
528
|
+
.catch(error => ({ index, error, status: 'rejected' }))
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
// 使用 Promise.race 获取第一个完成的结果
|
|
532
|
+
return await Promise.race(promises);
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* 异步触发事件,瀑布流执行
|
|
537
|
+
* @param {String} key 事件关键字,支持namespace:key格式
|
|
538
|
+
* @param {*} value 初始值
|
|
539
|
+
* @param {...any} args 事件参数
|
|
540
|
+
* @returns {Promise} - 最后一个事件监听者的结果
|
|
541
|
+
*/
|
|
542
|
+
Eventer.prototype.emitWaterfall = async function (key, value, ...args) {
|
|
543
|
+
// 解析命名空间
|
|
544
|
+
var { namespace, key } = this._parseKey(key);
|
|
545
|
+
var fullkey = `${namespace}:${key}`;
|
|
546
|
+
|
|
547
|
+
// 获取事件列表
|
|
548
|
+
var list = this._dict[namespace] && this._dict[namespace][key];
|
|
549
|
+
|
|
550
|
+
if (!list || list.length === 0) {
|
|
551
|
+
return value;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
let val = value;
|
|
555
|
+
|
|
556
|
+
// 按优先级排序
|
|
557
|
+
var sortedList = [...list].sort((a, b) => a.priority - b.priority);
|
|
558
|
+
|
|
559
|
+
for (const handler of sortedList) {
|
|
560
|
+
try {
|
|
561
|
+
val = await handler.func(val, ...args);
|
|
562
|
+
} catch (error) {
|
|
563
|
+
this.logger('error', `瀑布流执行失败 [${fullkey}]:`, error);
|
|
564
|
+
throw error;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return val;
|
|
569
|
+
};
|
|
570
|
+
|
|
411
571
|
/**
|
|
412
572
|
* 触发所有命名空间的特定事件
|
|
413
573
|
* @param {String} key 事件关键字(不含命名空间)
|
|
414
574
|
* @param {Object} params 传递参数
|
|
415
575
|
* @returns {Promise<Object>} 返回各命名空间的执行结果 {namespace1: results1, namespace2: results2}
|
|
416
576
|
*/
|
|
417
|
-
Eventer.prototype.
|
|
577
|
+
Eventer.prototype.emitAllNamespaces = async function (key, ...params) {
|
|
418
578
|
var results = {};
|
|
419
579
|
// 确保遍历所有命名空间,包括动态创建的
|
|
420
580
|
for (var name in this._dict) {
|
|
@@ -770,47 +930,47 @@ Eventer.prototype.unchain = function (fromEvent, toEvents) {
|
|
|
770
930
|
var fullFromEvent = `${fromNamespace}:${fromkey}`;
|
|
771
931
|
|
|
772
932
|
// 如果源事件不存在事件链,直接返回
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
if (toEvents) {
|
|
778
|
-
// 移除特定目标事件
|
|
779
|
-
var targetEvents = Array.isArray(toEvents) ? toEvents : [toEvents];
|
|
780
|
-
for (var i = 0; i < targetEvents.length; i++) {
|
|
781
|
-
var toEvent = targetEvents[i];
|
|
782
|
-
if (typeof toEvent !== 'string') {
|
|
783
|
-
throw new Error('目标事件必须是非空字符串');
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// 解析目标事件
|
|
787
|
-
var { namespace: toNamespace, key: tokey } = this._parseKey(toEvent);
|
|
788
|
-
var fullToEvent = `${toNamespace}:${tokey}`;
|
|
933
|
+
if (!this._chain[fullFromEvent]) {
|
|
934
|
+
return this;
|
|
935
|
+
}
|
|
789
936
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
937
|
+
if (toEvents) {
|
|
938
|
+
// 移除特定目标事件
|
|
939
|
+
var targetEvents = Array.isArray(toEvents) ? toEvents : [toEvents];
|
|
940
|
+
for (var i = 0; i < targetEvents.length; i++) {
|
|
941
|
+
var toEvent = targetEvents[i];
|
|
942
|
+
if (typeof toEvent !== 'string') {
|
|
943
|
+
throw new Error('目标事件必须是非空字符串');
|
|
944
|
+
}
|
|
795
945
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
this.logger('warn', `事件链不存在: ${fullFromEvent} -> ${fullToEvent}`);
|
|
800
|
-
continue;
|
|
801
|
-
}
|
|
946
|
+
// 解析目标事件
|
|
947
|
+
var { namespace: toNamespace, key: tokey } = this._parseKey(toEvent);
|
|
948
|
+
var fullToEvent = `${toNamespace}:${tokey}`;
|
|
802
949
|
|
|
803
|
-
|
|
950
|
+
// 检查事件链是否存在
|
|
951
|
+
if (!this._chain[fullFromEvent]) {
|
|
952
|
+
this.logger('warn', `事件链不存在: ${fullFromEvent} -> ${fullToEvent}`);
|
|
953
|
+
continue;
|
|
804
954
|
}
|
|
805
955
|
|
|
806
|
-
//
|
|
807
|
-
|
|
808
|
-
|
|
956
|
+
// 查找并删除事件链
|
|
957
|
+
var index = this._chain[fullFromEvent].indexOf(fullToEvent);
|
|
958
|
+
if (index === -1) {
|
|
959
|
+
this.logger('warn', `事件链不存在: ${fullFromEvent} -> ${fullToEvent}`);
|
|
960
|
+
continue;
|
|
809
961
|
}
|
|
810
|
-
|
|
811
|
-
|
|
962
|
+
|
|
963
|
+
this._chain[fullFromEvent].splice(index, 1);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// 如果事件链为空,则删除整个事件链
|
|
967
|
+
if (this._chain[fullFromEvent].length === 0) {
|
|
812
968
|
delete this._chain[fullFromEvent];
|
|
813
969
|
}
|
|
970
|
+
} else {
|
|
971
|
+
// 移除整个事件链
|
|
972
|
+
delete this._chain[fullFromEvent];
|
|
973
|
+
}
|
|
814
974
|
|
|
815
975
|
return this;
|
|
816
976
|
};
|