/** * 唤醒脉冲 —— **人答完了就立刻接着跑**,不必等下一轮轮询。 * * 两个 worker 都是 2 秒一轮(派发器投递事件、推进器推 running 的单)。 * 平时够用:后台自己转,快两秒慢两秒没人看着。可**人刚点完一张卡**那一下不同 —— * 他正盯着屏幕等反应。实测点「确认写入」到引擎真转起来要 **6.8 秒** * (投递等一轮 + 推进等一轮),这 6.8 秒里界面上一动不动: * 办事条只认 `runtime === 'running'`,而那时它还是 `awaiting-event`。 * 人看到的是「我点了,没反应」——于是再点一次。 * * 所以给路由一个踢一脚的口子:回传落库后 `void pulse()`,两个 worker 就地各跑一轮。 * **不 await**:推进一拍要调模型,秒级;回传该立刻返回,推进在后台接着办。 * * 合并而不排队:正在跑就记一个「还要再来一轮」,跑完补一轮就够 —— * 连点五下不该叠出五轮推进。 */ type Tick = () => Promise; const ticks: Tick[] = []; let running = false; let again = false; /** 由 `main.ts` 在启动时登记(顺序有意义:先投递、后推进) */ export function registerPulse(tick: Tick): void { ticks.push(tick); } /** 测试用:清空登记,免得用例之间互相串 */ export function resetPulse(): void { ticks.length = 0; running = false; again = false; } export async function pulse(): Promise { if (running) { again = true; return; } running = true; try { do { again = false; for (const tick of ticks) { try { await tick(); } catch (error) { // 一个 worker 出错不该拖垮另一个 —— 它下一轮定时还会再跑 process.stderr.write(`[pulse] ${error instanceof Error ? error.message : String(error)}\n`); } } } while (again); } finally { running = false; } }