diff --git a/CHANGELOG.md b/CHANGELOG.md index dc2739f..f9a28f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to `hy-event-store` are documented in this file. - Add first-party TypeScript declarations for event payloads, state keys, actions, and action return values, plus compile-time regression coverage. +- Allow off to cancel a pending once listener by using + the original callback reference. ## 1.4.0 - 2026-08-19 diff --git a/README.md b/README.md index eaf1083..f29436f 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ store.offStates(["count", "status"], renderProfile) | on(eventName, callback, thisArg?) | Register a listener. | The event bus | | once(eventName, callback, thisArg?) | Register a listener removed before its first callback runs. | The event bus | | emit(eventName, ...payload) | Notify every listener for an event. | The event bus | -| off(eventName, callback) | Remove registrations for the exact callback. It is safe when the event has no listeners. | The event bus | +| off(eventName, callback) | Remove registrations for the callback passed to on or once. It is safe when the event has no listeners. | The event bus | | clear() | Remove listeners for every event. | The event bus | | hasEvent(eventName) | Check whether an event has at least one listener. | Boolean | diff --git a/README.zh-CN.md b/README.zh-CN.md index d66e3fb..2851ba3 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -150,7 +150,7 @@ store.offStates(["count", "status"], renderProfile) | on(eventName, callback, thisArg?) | 注册监听器。 | 事件总线实例 | | once(eventName, callback, thisArg?) | 注册仅在第一次触发时执行的监听器。 | 事件总线实例 | | emit(eventName, ...payload) | 通知某个事件的全部监听器。 | 事件总线实例 | -| off(eventName, callback) | 移除指定回调的监听;事件不存在时也可安全调用。 | 事件总线实例 | +| off(eventName, callback) | 移除通过 on 或 once 注册的回调;事件不存在时也可安全调用。 | 事件总线实例 | | clear() | 清除全部事件的监听器。 | 事件总线实例 | | hasEvent(eventName) | 判断事件是否至少有一个监听器。 | 布尔值 | diff --git a/Test/event-bus.test.js b/Test/event-bus.test.js index e116f54..d07a81b 100644 --- a/Test/event-bus.test.js +++ b/Test/event-bus.test.js @@ -29,6 +29,21 @@ test("once does not skip the next handler", () => { assert.deepEqual(calls, ["once", "always", "always"]) }) +test("off cancels a once listener by its original callback", () => { + const bus = new HYEventBus() + let calls = 0 + const callback = () => { + calls += 1 + } + + bus.once("update", callback) + bus.off("update", callback) + bus.emit("update") + + assert.equal(calls, 0) + assert.equal(bus.hasEvent("update"), false) +}) + test("off is safe for missing events and removes matching handlers", () => { const bus = new HYEventBus() const callback = () => {} diff --git a/src/event-bus.js b/src/event-bus.js index 6a09670..a5fe1c7 100644 --- a/src/event-bus.js +++ b/src/event-bus.js @@ -20,6 +20,7 @@ class HYEventBus { handlers.push({ eventCallback, + originalCallback: eventCallback, thisArg }) return this @@ -39,7 +40,9 @@ class HYEventBus { eventCallback.apply(thisArg, payload) } - return this.on(eventName, tempCallback, thisArg) + this.on(eventName, tempCallback, thisArg) + this.eventBus[eventName][this.eventBus[eventName].length - 1].originalCallback = eventCallback + return this } emit(eventName, ...payload) { @@ -70,7 +73,7 @@ class HYEventBus { } this.eventBus[eventName] = handlers.filter(handler => { - return handler.eventCallback !== eventCallback + return handler.eventCallback !== eventCallback && handler.originalCallback !== eventCallback }) if (this.eventBus[eventName].length === 0) {