重计算场景下Dart Timer.periodic与JS setInterval的行为差异疑问
问题解答
核心差异:Dart Timer.periodic 与 JS setInterval 调度规则不同
你观察到的现象是Dart定时器的固有设计,以下是逐步骤执行逻辑说明:
实际执行流程(对应你的Dart测试代码)
- 程序进入
asyncAwait函数,首先注册Timer.periodic(Duration(seconds: 1), callback),记注册时间为T0。Dart会为该定时器生成固定的预定触发时间序列:T0+1000ms、T0+2000ms、T0+3000ms…… 下一次的预定触发时间永远是上一次的预定触发时间加固定间隔,和回调的实际执行时间无关。 - 程序进入100亿次的while循环执行重计算,单线程完全阻塞,事件循环停止处理所有异步任务,这个过程耗时5988ms,结束时当前时间为
T0+5988ms。 - 重计算结束,主线程空闲,事件循环开始处理任务:
- 检查定时器队列,发现当前时间已经远超过定时器的第一个有效预定触发时间
T0+5000ms,于是将第一个回调加入事件队列,同时将该定时器的下一次预定触发时间更新为T0+6000ms。 - 执行第一个回调,打印
1 at 5997ms Diff: null,执行完成后当前时间为T0+6005ms。 - 事件循环再次检查定时器队列,发现当前时间已经超过了更新后的预定触发时间
T0+6000ms,于是立即将第二个回调加入事件队列,同时将下一次预定触发时间更新为T0+7000ms。 - 执行第二个回调,打印
2 at 6005ms Diff: 8,这就是你看到的间隔极短的原因。 - 事件循环再次检查定时器队列,发现下一次预定触发时间
T0+7000ms还未到,于是进入等待状态。 - 到
T0+7007ms时触发第三个回调,之后所有回调都对齐预定的1秒间隔,直到x=5时取消定时器。
- 检查定时器队列,发现当前时间已经远超过定时器的第一个有效预定触发时间
你的认知错误点
你默认Timer.periodic的下一次触发时间是从上一次回调执行完成后才开始计算间隔,这是JSsetInterval的逻辑,不是Dart的逻辑。Dart的periodic定时器始终对齐注册时生成的固定时间序列,即使阻塞错过多个时间点,也会在阻塞结束后尽快补触发到最近的有效时间点,之后恢复正常间隔。
为什么JS测试结果符合你的预期
JS的setInterval逻辑是:如果到了触发时间主线程仍被阻塞,就直接丢弃本次触发;阻塞结束后触发一次,下一次的触发时间以本次实际执行时间为基准加1000ms,因此不会出现连续补触发的情况。
内容的提问来源于stack exchange,提问作者TSR
相关产品推荐
相关产品推荐

