You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dart中Future.delayed抛错被Timer回调捕获的不稳定问题

为什么Dart中Timer延迟1ms注册Future错误回调有时失败?

先看你提供的代码:

var future = Future<int>.delayed(
    const Duration(milliseconds: 220), () => throw Exception("Hello!"));
Timer(const Duration(milliseconds: 219),
    () => future.then(print, onError: (err) => print(err)));

核心原因:事件循环调度与未处理异步错误规则

这不是Dart时间计算不精准的问题,而是由操作系统定时器精度和Dart的事件循环+未处理错误机制共同导致的:

  1. 任务的调度逻辑

    • Future.delayed 和 Timer 都会将任务添加到Dart的事件队列中,等待指定延迟后执行。但操作系统的定时器本身存在精度限制(多数系统的定时器精度在1~10ms之间),加上单线程事件循环一次只能处理一个任务,当两个任务的延迟时间差极小时(比如1ms),谁先进入事件队列并执行是不确定的。
  2. 两种执行场景

    • 场景1:Timer回调先执行
      Timer的回调先触发,此时future还处于未完成状态。调用future.then(...)相当于提前给这个Future注册了错误回调,等后续Future执行抛出错误时,回调会被正常触发,因此能捕获到错误。
    • 场景2:Future任务先执行
      Future的任务先触发并抛出错误,此时还没有任何错误回调被注册。Dart会将这个错误标记为未处理异步错误,并在当前事件处理完成后检查未处理错误列表,输出错误信息。之后Timer回调执行时再注册onError回调,此时错误已经被标记为未处理,不会再触发回调,因此你看不到预期的错误输出。
  3. 增大延迟差为什么有效?
    当把Timer的延迟改为100ms时,Timer回调的触发时间远早于Future的任务,能确保在Future抛出错误前就完成回调注册,因此每次都能捕获到错误。

正确的错误捕获方式

如果要确保Future的错误被捕获,建议在创建Future时直接注册回调,或者使用async/await语法,避免依赖Timer的延迟调度:

方式1:直接链式调用

var future = Future<int>.delayed(
  const Duration(milliseconds: 220), 
  () => throw Exception("Hello!")
).then(print, onError: (err) => print(err));

方式2:使用async/await

void main() async {
  try {
    await Future<int>.delayed(
      const Duration(milliseconds: 220), 
      () => throw Exception("Hello!")
    );
  } catch (err) {
    print(err);
  }
}

内容的提问来源于stack exchange,提问作者Anonymous

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 13:01:41