diff --git a/cocoscreator_projects/YouleNexus/assets/framework/core/events.ts b/cocoscreator_projects/YouleNexus/assets/framework/core/events.ts new file mode 100644 index 0000000..34d10a4 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/core/events.ts @@ -0,0 +1,29 @@ +type Handler = (...args: any[]) => void; + +/** 轻量类型化事件总线。订阅者抛错被隔离,不影响其它订阅者(故障隔离)。 */ +export class EventBus = Record> { + private map = new Map>(); + + on(type: K, fn: (...args: Events[K]) => void): void { + let set = this.map.get(type); + if (!set) { set = new Set(); this.map.set(type, set); } + set.add(fn as Handler); + } + + once(type: K, fn: (...args: Events[K]) => void): void { + const wrap = (...args: Events[K]) => { this.off(type, wrap as any); (fn as any)(...args); }; + this.on(type, wrap as any); + } + + off(type: K, fn: (...args: Events[K]) => void): void { + this.map.get(type)?.delete(fn as Handler); + } + + emit(type: K, ...args: Events[K]): void { + const set = this.map.get(type); + if (!set) return; + for (const fn of [...set]) { + try { fn(...args); } catch (e) { console.error('[EventBus] handler error:', e); } + } + } +} diff --git a/cocoscreator_projects/framework-tests/core/events.test.ts b/cocoscreator_projects/framework-tests/core/events.test.ts new file mode 100644 index 0000000..879d560 --- /dev/null +++ b/cocoscreator_projects/framework-tests/core/events.test.ts @@ -0,0 +1,40 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { EventBus } from '../../YouleNexus/assets/framework/core/events.ts'; + +test('on/emit 传递参数', () => { + const bus = new EventBus<{ hi: [number, string] }>(); + let got: [number, string] | null = null; + bus.on('hi', (n, s) => { got = [n, s]; }); + bus.emit('hi', 7, 'x'); + assert.deepEqual(got, [7, 'x']); +}); + +test('off 取消订阅', () => { + const bus = new EventBus<{ ping: [] }>(); + let count = 0; + const fn = () => { count++; }; + bus.on('ping', fn); + bus.emit('ping'); + bus.off('ping', fn); + bus.emit('ping'); + assert.equal(count, 1); +}); + +test('once 只触发一次', () => { + const bus = new EventBus<{ ping: [] }>(); + let count = 0; + bus.once('ping', () => { count++; }); + bus.emit('ping'); + bus.emit('ping'); + assert.equal(count, 1); +}); + +test('一个事件订阅者抛错不影响其它订阅者', () => { + const bus = new EventBus<{ ping: [] }>(); + let reached = false; + bus.on('ping', () => { throw new Error('boom'); }); + bus.on('ping', () => { reached = true; }); + bus.emit('ping'); + assert.equal(reached, true); +});