DEBUG.WIKI
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →
雨云;给项目一台云服务器
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →

RxJS timer 启动后另一个 setInterval 停了,检查 asapScheduler

RxJS7.8.2从零延迟asap任务转正数周期时,可能将微任务句柄交给clearInterval。按实际时序核对;普通轮询可采用默认async调度,但首次执行顺序会改变。

原生 setInterval 没有被主动清除,却在订阅一个 RxJS timer 后停止。如果 timer 的形态是 timer(0, 正数周期, asapScheduler),需要检查 RxJS 7.8.2 的零延迟到正延迟切换路径。

asapScheduler 的零延迟任务使用独立递增的微任务句柄;进入后续正数周期时,AsapAction 又把旧句柄交给 AsyncAction 的回收逻辑,后者调用 clearInterval。若这个数字恰好对应运行环境中的其他 interval,就可能清掉与当前 RxJS 订阅无关的计时器。

首次发送与重复调度使用了不同句柄

正式 7.8.2 的 timer 先按 dueTime 调度,第一次发送后,再用同一个 action 按 intervalDuration 重新调度。AsapAction 根据新的正数 delay 选择异步回收分支;此时旧 id 却可能来自它自己的 Immediate 微任务计数器,而非 setInterval。

因此,必须同时保留首次延迟为零、后续周期为正、显式 asapScheduler 以及其他计时器的创建顺序。句柄是否相撞与环境和时序有关,不能保证随便订阅一次就能复现,也不能把所有定时器停止都归为 RxJS。

还要区分“没有再触发”与“继续触发但读到旧值”。后者可能属于闭包中的旧 state,不应通过更换调度器掩盖状态读取问题。

周期任务不需要微任务起步时,保留默认调度器

若需求只是尽快开始一个周期轮询,没有依赖微任务先于计时器的执行顺序,可以移除第三个 asapScheduler 参数,让整个 timer 使用默认 asyncScheduler:

import { timer } from 'rxjs';

const subscription = timer(0, 10_000).subscribe({
  next: value => {
    // 调用原有的周期处理函数。
    handleTick(value);
  },
  error: error => reportTimerError(error),
});

// 在原有组件、任务或服务的清理阶段执行。
// subscription.unsubscribe();

handleTick、reportTimerError 代表现有处理函数。这里改变了第一次发送的调度时机:它进入计时器调度,不再是 asap 的微任务路径。需要微任务先完成某项状态变更的流程,应先重新设计该顺序,不能照抄后假定完全等价。

不要把首次延迟随意改成一个很小的正数,再宣称已修复底层句柄问题;这仍然是改变时序的规避。也不要全局替换 clearInterval,或猜测某个句柄属于谁后忽略清理,那会影响其他订阅和原生计时器。

7.8.2 的正式源码仍保留上述分支。选用新版本时,应确认实际安装包中这条切换路径已改变,而非仅凭某个分支或修复提议认为正式版已修。现有规避只针对这类混合调度,不要求删除所有 asapScheduler 使用。

验收时同时观察原生 interval 和 RxJS 的连续发送,使用应用实际的运行环境与创建顺序。解除 RxJS 订阅应只停止它自己的周期,原生 interval 保持原有生命周期;原生计时器最终仍由其所有者正常清理。不要只观察第一条 tick 就认定没有后续干扰。