M5(T-M5-03): 高频出站节流——通用 Debouncer/Throttler + 网络变化防抖合并

- common 新增 Debouncer/Throttler 工具(不在桥核心按 handler 名节流,避免桥核心
  感知能力,违反零感知约束;由 Provider 自治应用)
- NetworkProvider:netAvailable/netLost/netCapabilitiesChange 三事件统一经 300ms
  防抖,切网瞬间多次突发合并为一次出站;onBackground/onDestroy 取消待发
- 说明:连续定位已受系统 timeInterval:5(5s) 约束、Device 为被动查询,无需额外节流;
  lzyzsd 协议 _handleMessageFromNative 单条语义,不做跨条合并(保 H5 零改动)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lanterngamescn
2026-06-26 07:11:48 +08:00
co-authored by Claude Opus 4.8
parent b461b291f5
commit 0a372f1339
3 changed files with 89 additions and 4 deletions
+76
View File
@@ -0,0 +1,76 @@
/**
* 节流 / 防抖工具(T-M5-03)。供高频出站源(如网络变化广播)合并突发、降低跨引擎调用次数。
*
* 不在桥核心做按 handler 名的节流(那会让桥核心感知具体能力,违反「桥核心零感知」约束);
* 由各 Provider 自治决定对哪些可合并的状态广播应用本工具。
*/
/** 防抖:连续触发只在最后一次后 delayMs 执行一次(合并突发,取最终状态)。 */
export class Debouncer {
private readonly delayMs: number;
private timer: number = -1;
constructor(delayMs: number) {
this.delayMs = delayMs;
}
/** 触发:取消上一次待执行,重新计时;窗口静默 delayMs 后执行 action。 */
run(action: () => void): void {
this.cancel();
this.timer = setTimeout(() => {
this.timer = -1;
action();
}, this.delayMs);
}
/** 取消未执行的回调(Provider 去激活/销毁时调用,防泄漏与打已弃桥)。 */
cancel(): void {
if (this.timer !== -1) {
clearTimeout(this.timer);
this.timer = -1;
}
}
}
/** 限流:窗口 windowMs 内最多执行一次(leading),窗口内最后一次在窗口末补执行(trailing)。 */
export class Throttler {
private readonly windowMs: number;
private last: number = 0;
private timer: number = -1;
private pending: (() => void) | undefined = undefined;
constructor(windowMs: number) {
this.windowMs = windowMs;
}
/** 触发:距上次执行 ≥ windowMs 立即执行;否则记为待执行,窗口末执行最后一次。 */
run(action: () => void): void {
const now: number = Date.now();
const elapsed: number = now - this.last;
if (elapsed >= this.windowMs) {
this.last = now;
action();
return;
}
this.pending = action;
if (this.timer === -1) {
this.timer = setTimeout(() => {
this.timer = -1;
this.last = Date.now();
const p = this.pending;
this.pending = undefined;
if (p !== undefined) {
p();
}
}, this.windowMs - elapsed);
}
}
cancel(): void {
if (this.timer !== -1) {
clearTimeout(this.timer);
this.timer = -1;
}
this.pending = undefined;
}
}