装饰器事件自动清理
This commit is contained in:
@@ -469,39 +469,114 @@ export class GlobalEventBus {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 事件装饰器工厂
|
* 事件装饰器工厂
|
||||||
* 用于自动注册事件监听器
|
* 用于自动注册事件监听器,支持自动清理
|
||||||
*/
|
*/
|
||||||
export function EventHandler(eventType: string, config: IEventListenerConfig = {}) {
|
export function EventHandler(eventType: string, config: IEventListenerConfig = {}) {
|
||||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||||
const originalMethod = descriptor.value;
|
const originalMethod = descriptor.value;
|
||||||
|
|
||||||
|
// 存储装饰器信息
|
||||||
|
if (!target.constructor._eventHandlers) {
|
||||||
|
target.constructor._eventHandlers = [];
|
||||||
|
}
|
||||||
|
target.constructor._eventHandlers.push({
|
||||||
|
eventType,
|
||||||
|
methodName: propertyKey,
|
||||||
|
config
|
||||||
|
});
|
||||||
|
|
||||||
// 在类实例化时自动注册监听器
|
// 在类实例化时自动注册监听器
|
||||||
const initMethod = target.constructor.prototype.initEventListeners || function() {};
|
const initMethod = target.constructor.prototype.initEventListeners || function() {};
|
||||||
target.constructor.prototype.initEventListeners = function() {
|
target.constructor.prototype.initEventListeners = function() {
|
||||||
initMethod.call(this);
|
initMethod.call(this);
|
||||||
|
|
||||||
|
// 初始化监听器追踪数组
|
||||||
|
if (!this._decoratorEventListeners) {
|
||||||
|
this._decoratorEventListeners = [];
|
||||||
|
}
|
||||||
|
|
||||||
const eventBus = GlobalEventBus.getInstance();
|
const eventBus = GlobalEventBus.getInstance();
|
||||||
eventBus.on(eventType, originalMethod.bind(this), config);
|
const listenerId = eventBus.on(eventType, originalMethod.bind(this), config);
|
||||||
|
|
||||||
|
// 保存监听器ID用于后续清理
|
||||||
|
this._decoratorEventListeners.push({
|
||||||
|
eventType,
|
||||||
|
methodName: propertyKey,
|
||||||
|
listenerId
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 添加清理方法
|
||||||
|
const cleanupMethod = target.constructor.prototype.cleanupEventListeners || function() {};
|
||||||
|
target.constructor.prototype.cleanupEventListeners = function() {
|
||||||
|
cleanupMethod.call(this);
|
||||||
|
|
||||||
|
if (this._decoratorEventListeners) {
|
||||||
|
const eventBus = GlobalEventBus.getInstance();
|
||||||
|
for (const listener of this._decoratorEventListeners) {
|
||||||
|
eventBus.off(listener.eventType, listener.listenerId);
|
||||||
|
}
|
||||||
|
this._decoratorEventListeners.length = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return descriptor;
|
return descriptor;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异步事件装饰器工厂
|
* 异步事件装饰器工厂
|
||||||
* 用于自动注册异步事件监听器
|
* 用于自动注册异步事件监听器,支持自动清理
|
||||||
*/
|
*/
|
||||||
export function AsyncEventHandler(eventType: string, config: IEventListenerConfig = {}) {
|
export function AsyncEventHandler(eventType: string, config: IEventListenerConfig = {}) {
|
||||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||||
const originalMethod = descriptor.value;
|
const originalMethod = descriptor.value;
|
||||||
|
|
||||||
|
// 存储装饰器信息
|
||||||
|
if (!target.constructor._eventHandlers) {
|
||||||
|
target.constructor._eventHandlers = [];
|
||||||
|
}
|
||||||
|
target.constructor._eventHandlers.push({
|
||||||
|
eventType,
|
||||||
|
methodName: propertyKey,
|
||||||
|
config,
|
||||||
|
async: true
|
||||||
|
});
|
||||||
|
|
||||||
const initMethod = target.constructor.prototype.initEventListeners || function() {};
|
const initMethod = target.constructor.prototype.initEventListeners || function() {};
|
||||||
target.constructor.prototype.initEventListeners = function() {
|
target.constructor.prototype.initEventListeners = function() {
|
||||||
initMethod.call(this);
|
initMethod.call(this);
|
||||||
|
|
||||||
|
// 初始化监听器追踪数组
|
||||||
|
if (!this._decoratorEventListeners) {
|
||||||
|
this._decoratorEventListeners = [];
|
||||||
|
}
|
||||||
|
|
||||||
const eventBus = GlobalEventBus.getInstance();
|
const eventBus = GlobalEventBus.getInstance();
|
||||||
eventBus.onAsync(eventType, originalMethod.bind(this), config);
|
const listenerId = eventBus.onAsync(eventType, originalMethod.bind(this), config);
|
||||||
|
|
||||||
|
// 保存监听器ID用于后续清理
|
||||||
|
this._decoratorEventListeners.push({
|
||||||
|
eventType,
|
||||||
|
methodName: propertyKey,
|
||||||
|
listenerId
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 添加清理方法
|
||||||
|
const cleanupMethod = target.constructor.prototype.cleanupEventListeners || function() {};
|
||||||
|
target.constructor.prototype.cleanupEventListeners = function() {
|
||||||
|
cleanupMethod.call(this);
|
||||||
|
|
||||||
|
if (this._decoratorEventListeners) {
|
||||||
|
const eventBus = GlobalEventBus.getInstance();
|
||||||
|
for (const listener of this._decoratorEventListeners) {
|
||||||
|
eventBus.off(listener.eventType, listener.listenerId);
|
||||||
|
}
|
||||||
|
this._decoratorEventListeners.length = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return descriptor;
|
return descriptor;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,7 @@ interface EventListenerRecord {
|
|||||||
listenerRef: string;
|
listenerRef: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体系统的基类
|
* 实体系统的基类
|
||||||
*
|
*
|
||||||
@@ -52,6 +53,10 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
private _scene: Scene | null;
|
private _scene: Scene | null;
|
||||||
protected logger = createLogger('EntitySystem');
|
protected logger = createLogger('EntitySystem');
|
||||||
|
|
||||||
|
// 装饰器动态添加的方法(可选)
|
||||||
|
protected initEventListeners?: () => void;
|
||||||
|
protected cleanupEventListeners?: () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体ID映射缓存
|
* 实体ID映射缓存
|
||||||
*/
|
*/
|
||||||
@@ -134,6 +139,9 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
this._entityIdMapVersion = -1;
|
this._entityIdMapVersion = -1;
|
||||||
this._entityIdMapSize = 0;
|
this._entityIdMapSize = 0;
|
||||||
|
|
||||||
|
// 初始化装饰器事件监听器
|
||||||
|
this.initDecoratorEventListeners();
|
||||||
|
|
||||||
this._entityCache = {
|
this._entityCache = {
|
||||||
frame: null,
|
frame: null,
|
||||||
persistent: null,
|
persistent: null,
|
||||||
@@ -238,10 +246,8 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
this._entityIdMapSize = 0;
|
this._entityIdMapSize = 0;
|
||||||
|
|
||||||
// 清理所有事件监听器
|
// 清理所有事件监听器
|
||||||
this.cleanupEventListeners();
|
// 调用框架销毁方法
|
||||||
|
this.destroy();
|
||||||
// 调用用户可重写的销毁方法
|
|
||||||
this.onDestroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -746,12 +752,29 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清理所有事件监听器
|
* 初始化装饰器事件监听器
|
||||||
*
|
|
||||||
* 系统移除时自动调用,清理所有通过addEventListener添加的监听器。
|
|
||||||
*/
|
*/
|
||||||
private cleanupEventListeners(): void {
|
protected initDecoratorEventListeners(): void {
|
||||||
|
if (this.initEventListeners) {
|
||||||
|
this.initEventListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理装饰器事件监听器
|
||||||
|
*/
|
||||||
|
protected cleanupDecoratorEventListeners(): void {
|
||||||
|
if (this.cleanupEventListeners) {
|
||||||
|
this.cleanupEventListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理手动添加的事件监听器
|
||||||
|
*/
|
||||||
|
private cleanupManualEventListeners(): void {
|
||||||
for (const listener of this._eventListeners) {
|
for (const listener of this._eventListeners) {
|
||||||
try {
|
try {
|
||||||
listener.eventSystem.off(listener.eventType, listener.listenerRef);
|
listener.eventSystem.off(listener.eventType, listener.listenerRef);
|
||||||
@@ -765,7 +788,22 @@ export abstract class EntitySystem implements ISystemBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统销毁时的回调
|
* 框架内部销毁方法
|
||||||
|
* 由框架调用,处理系统的完整销毁流程
|
||||||
|
*/
|
||||||
|
public destroy(): void {
|
||||||
|
// 1. 清理手动添加的事件监听器
|
||||||
|
this.cleanupManualEventListeners();
|
||||||
|
|
||||||
|
// 2. 清理装饰器事件监听器
|
||||||
|
this.cleanupDecoratorEventListeners();
|
||||||
|
|
||||||
|
// 3. 调用用户的销毁回调
|
||||||
|
this.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户销毁回调
|
||||||
*
|
*
|
||||||
* 当系统从场景中移除时调用,子类可以重写此方法进行清理操作。
|
* 当系统从场景中移除时调用,子类可以重写此方法进行清理操作。
|
||||||
* 注意:事件监听器会被框架自动清理,无需手动处理。
|
* 注意:事件监听器会被框架自动清理,无需手动处理。
|
||||||
|
|||||||
326
packages/core/tests/ECS/Systems/EntitySystem.test.ts
Normal file
326
packages/core/tests/ECS/Systems/EntitySystem.test.ts
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem';
|
||||||
|
import { Entity } from '../../../src/ECS/Entity';
|
||||||
|
import { Component } from '../../../src/ECS/Component';
|
||||||
|
import { Scene } from '../../../src/ECS/Scene';
|
||||||
|
import { Matcher } from '../../../src/ECS/Utils/Matcher';
|
||||||
|
import { EventHandler, AsyncEventHandler, GlobalEventBus } from '../../../src/ECS/Core/EventBus';
|
||||||
|
import { TypeSafeEventSystem } from '../../../src/ECS/Core/EventSystem';
|
||||||
|
|
||||||
|
// 测试组件
|
||||||
|
class TestComponent extends Component {
|
||||||
|
public value: number = 0;
|
||||||
|
|
||||||
|
constructor(...args: unknown[]) {
|
||||||
|
super();
|
||||||
|
const [value = 0] = args as [number?];
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试事件
|
||||||
|
interface TestEvent {
|
||||||
|
id: number;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AsyncTestEvent {
|
||||||
|
data: string;
|
||||||
|
timestamp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 具体的实体系统实现
|
||||||
|
class ConcreteEntitySystem extends EntitySystem {
|
||||||
|
public processCallCount = 0;
|
||||||
|
public eventHandlerCallCount = 0;
|
||||||
|
public asyncEventHandlerCallCount = 0;
|
||||||
|
public lastEvent: TestEvent | null = null;
|
||||||
|
public lastAsyncEvent: AsyncTestEvent | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.all(TestComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(entities: readonly Entity[]): void {
|
||||||
|
this.processCallCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler('test_event')
|
||||||
|
onTestEvent(event: TestEvent): void {
|
||||||
|
this.eventHandlerCallCount++;
|
||||||
|
this.lastEvent = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AsyncEventHandler('async_test_event')
|
||||||
|
async onAsyncTestEvent(event: AsyncTestEvent): Promise<void> {
|
||||||
|
this.asyncEventHandlerCallCount++;
|
||||||
|
this.lastAsyncEvent = event;
|
||||||
|
// 模拟异步操作
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用的手动事件监听器
|
||||||
|
public addManualEventListener(): void {
|
||||||
|
this.addEventListener('manual_event', (event: any) => {
|
||||||
|
this.eventHandlerCallCount++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用的移除事件监听器
|
||||||
|
public removeManualEventListener(): void {
|
||||||
|
const handler = (event: any) => {
|
||||||
|
this.eventHandlerCallCount++;
|
||||||
|
};
|
||||||
|
this.addEventListener('manual_event', handler);
|
||||||
|
this.removeEventListener('manual_event', handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 公开测试方法
|
||||||
|
public testAddEventListener(eventType: string, handler: (event: any) => void): void {
|
||||||
|
this.addEventListener(eventType, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public testRemoveEventListener(eventType: string, handler: (event: any) => void): void {
|
||||||
|
this.removeEventListener(eventType, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有装饰器的系统(用于对比测试)
|
||||||
|
class PlainEntitySystem extends EntitySystem {
|
||||||
|
public processCallCount = 0;
|
||||||
|
public onDestroyCallCount = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.all(TestComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(entities: readonly Entity[]): void {
|
||||||
|
this.processCallCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override onDestroy(): void {
|
||||||
|
this.onDestroyCallCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('EntitySystem', () => {
|
||||||
|
let scene: Scene;
|
||||||
|
let system: ConcreteEntitySystem;
|
||||||
|
let entity: Entity;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
scene = new Scene();
|
||||||
|
system = new ConcreteEntitySystem();
|
||||||
|
entity = new Entity('test_entity', 1);
|
||||||
|
entity.addComponent(new TestComponent(10));
|
||||||
|
|
||||||
|
scene.addEntity(entity);
|
||||||
|
scene.addSystem(system);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// 清理场景
|
||||||
|
scene.removeSystem(system);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('装饰器事件处理', () => {
|
||||||
|
it('应该自动注册 @EventHandler 装饰器标记的方法', () => {
|
||||||
|
const testEvent: TestEvent = { id: 1, message: 'test' };
|
||||||
|
|
||||||
|
// 发射事件(使用全局事件总线,因为装饰器注册在那里)
|
||||||
|
GlobalEventBus.getInstance().emit('test_event', testEvent);
|
||||||
|
|
||||||
|
// 验证事件处理器被调用
|
||||||
|
expect(system.eventHandlerCallCount).toBe(1);
|
||||||
|
expect(system.lastEvent).toEqual(testEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该自动注册 @AsyncEventHandler 装饰器标记的方法', async () => {
|
||||||
|
const asyncEvent: AsyncTestEvent = {
|
||||||
|
data: 'async test',
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发射异步事件(使用全局事件总线)
|
||||||
|
await GlobalEventBus.getInstance().emitAsync('async_test_event', asyncEvent);
|
||||||
|
|
||||||
|
// 等待异步处理完成
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 20));
|
||||||
|
|
||||||
|
// 验证异步事件处理器被调用
|
||||||
|
expect(system.asyncEventHandlerCallCount).toBe(1);
|
||||||
|
expect(system.lastAsyncEvent).toEqual(asyncEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('当系统被销毁时,应该自动清理装饰器事件监听器', () => {
|
||||||
|
const testEvent: TestEvent = { id: 2, message: 'test after destroy' };
|
||||||
|
|
||||||
|
// 验证事件监听器工作正常
|
||||||
|
GlobalEventBus.getInstance().emit('test_event', testEvent);
|
||||||
|
expect(system.eventHandlerCallCount).toBe(1);
|
||||||
|
|
||||||
|
// 销毁系统
|
||||||
|
scene.removeSystem(system);
|
||||||
|
|
||||||
|
// 重置计数器并再次发射事件
|
||||||
|
system.eventHandlerCallCount = 0;
|
||||||
|
GlobalEventBus.getInstance().emit('test_event', testEvent);
|
||||||
|
|
||||||
|
// 验证事件监听器已被清理,不再响应事件
|
||||||
|
expect(system.eventHandlerCallCount).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('手动事件监听器管理', () => {
|
||||||
|
it('应该能够手动添加事件监听器', () => {
|
||||||
|
system.addManualEventListener();
|
||||||
|
|
||||||
|
// 发射手动事件
|
||||||
|
scene.eventSystem.emitSync('manual_event', { test: true });
|
||||||
|
|
||||||
|
// 验证手动事件监听器被调用
|
||||||
|
expect(system.eventHandlerCallCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该能够手动移除事件监听器', () => {
|
||||||
|
const handler = jest.fn();
|
||||||
|
|
||||||
|
// 添加监听器
|
||||||
|
system.testAddEventListener('manual_remove_event', handler);
|
||||||
|
|
||||||
|
// 发射事件验证监听器工作
|
||||||
|
scene.eventSystem.emitSync('manual_remove_event', {});
|
||||||
|
expect(handler).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
// 移除监听器
|
||||||
|
system.testRemoveEventListener('manual_remove_event', handler);
|
||||||
|
|
||||||
|
// 再次发射事件
|
||||||
|
scene.eventSystem.emitSync('manual_remove_event', {});
|
||||||
|
|
||||||
|
// 验证监听器已被移除
|
||||||
|
expect(handler).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('当系统被销毁时,应该清理手动添加的事件监听器', () => {
|
||||||
|
const handler = jest.fn();
|
||||||
|
|
||||||
|
// 添加手动监听器
|
||||||
|
system.testAddEventListener('destroy_test_event', handler);
|
||||||
|
|
||||||
|
// 验证监听器工作
|
||||||
|
scene.eventSystem.emitSync('destroy_test_event', {});
|
||||||
|
expect(handler).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
// 销毁系统
|
||||||
|
scene.removeSystem(system);
|
||||||
|
|
||||||
|
// 再次发射事件
|
||||||
|
scene.eventSystem.emitSync('destroy_test_event', {});
|
||||||
|
|
||||||
|
// 验证监听器已被清理
|
||||||
|
expect(handler).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('生命周期管理', () => {
|
||||||
|
it('应该按正确的顺序执行销毁流程', () => {
|
||||||
|
const plainSystem = new PlainEntitySystem();
|
||||||
|
scene.addSystem(plainSystem);
|
||||||
|
|
||||||
|
// 销毁系统
|
||||||
|
scene.removeSystem(plainSystem);
|
||||||
|
|
||||||
|
// 验证用户的 onDestroy 被调用
|
||||||
|
expect(plainSystem.onDestroyCallCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('框架的 destroy 方法应该清理所有资源并调用用户回调', () => {
|
||||||
|
const testEvent: TestEvent = { id: 3, message: 'destroy test' };
|
||||||
|
const handler = jest.fn();
|
||||||
|
|
||||||
|
// 添加手动监听器
|
||||||
|
system.testAddEventListener('destroy_order_test', handler);
|
||||||
|
|
||||||
|
// 验证装饰器和手动监听器都工作
|
||||||
|
GlobalEventBus.getInstance().emit('test_event', testEvent);
|
||||||
|
scene.eventSystem.emitSync('destroy_order_test', {});
|
||||||
|
expect(system.eventHandlerCallCount).toBe(1);
|
||||||
|
expect(handler).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
// 直接调用框架的销毁方法
|
||||||
|
system.destroy();
|
||||||
|
|
||||||
|
// 重置计数器并验证所有监听器都被清理
|
||||||
|
system.eventHandlerCallCount = 0;
|
||||||
|
handler.mockClear();
|
||||||
|
|
||||||
|
GlobalEventBus.getInstance().emit('test_event', testEvent);
|
||||||
|
scene.eventSystem.emitSync('destroy_order_test', {});
|
||||||
|
|
||||||
|
expect(system.eventHandlerCallCount).toBe(0);
|
||||||
|
expect(handler).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('错误处理', () => {
|
||||||
|
it('当事件系统不可用时,应该安全地处理监听器注册', () => {
|
||||||
|
// 创建没有事件系统的场景
|
||||||
|
const sceneWithoutEvents = new Scene();
|
||||||
|
(sceneWithoutEvents as any).eventSystem = null;
|
||||||
|
|
||||||
|
const systemWithoutEvents = new ConcreteEntitySystem();
|
||||||
|
|
||||||
|
// 应该不会抛出错误
|
||||||
|
expect(() => {
|
||||||
|
sceneWithoutEvents.addSystem(systemWithoutEvents);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('当移除不存在的监听器时,应该安全处理', () => {
|
||||||
|
const nonExistentHandler = () => {};
|
||||||
|
|
||||||
|
// 应该不会抛出错误
|
||||||
|
expect(() => {
|
||||||
|
system.testRemoveEventListener('non_existent_event', nonExistentHandler);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('多个事件监听器', () => {
|
||||||
|
it('应该支持同一个系统监听多个不同的事件', () => {
|
||||||
|
const event1: TestEvent = { id: 1, message: 'first' };
|
||||||
|
const event2: AsyncTestEvent = { data: 'second', timestamp: Date.now() };
|
||||||
|
|
||||||
|
// 发射两个不同的事件
|
||||||
|
GlobalEventBus.getInstance().emit('test_event', event1);
|
||||||
|
GlobalEventBus.getInstance().emitAsync('async_test_event', event2);
|
||||||
|
|
||||||
|
// 等待异步处理
|
||||||
|
return new Promise<void>(resolve => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(system.eventHandlerCallCount).toBe(1);
|
||||||
|
expect(system.asyncEventHandlerCallCount).toBe(1);
|
||||||
|
expect(system.lastEvent).toEqual(event1);
|
||||||
|
expect(system.lastAsyncEvent).toEqual(event2);
|
||||||
|
resolve();
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该支持多个系统监听同一个事件', () => {
|
||||||
|
const system2 = new ConcreteEntitySystem();
|
||||||
|
scene.addSystem(system2);
|
||||||
|
|
||||||
|
const testEvent: TestEvent = { id: 4, message: 'shared event' };
|
||||||
|
|
||||||
|
// 发射事件
|
||||||
|
GlobalEventBus.getInstance().emit('test_event', testEvent);
|
||||||
|
|
||||||
|
// 验证两个系统都接收到了事件
|
||||||
|
expect(system.eventHandlerCallCount).toBe(1);
|
||||||
|
expect(system2.eventHandlerCallCount).toBe(1);
|
||||||
|
expect(system.lastEvent).toEqual(testEvent);
|
||||||
|
expect(system2.lastEvent).toEqual(testEvent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user