为什么 Future.timeout 在 dart 中不起作用?

Why does Future.timeout not work in dart?

我想 运行 计算 可能 花费太长时间。如果它没有在 特定时间 内完成,则中止计算并 return 一个不同的值 。对于这个问题,我发现 Future.timeout,它几乎可以做我想做的事情,除了它 不适用于此代码

Future<String> timeoutTest() async
{
  return await longComputation().timeout(
    Duration(milliseconds: 10),
    onTimeout: () => "Took too long"
  );
}

Future<String> longComputation() async
{
  int startTime = DateTime.now().millisecondsSinceEpoch;

  Rational n = Rational.one;
  for(int i = 1; i < 2000; i++)
  {
    n *= Rational.fromInt(i);
  }
  String result = n.toDecimalString();

  print("Time took: ${DateTime.now().millisecondsSinceEpoch - startTime} ms");
  return result;
}

当我调用 print(await timeoutTest()) 时,我要么期望 数字串 花费 最多 10 毫秒 来计算,要么 "Tok too long" 字符串,如果花费 超过 10 毫秒 。但是我得到了 数字串 ,并且控制台中的 消息 : "Time took: 877 ms".所以超时没有起作用。

如果我用 Future.delay 伪造计算,timeout 会按预期工作。它 return 是一个不同的值 ,因为 longComputation 至少花费了 100 毫秒 。 (我仍然在控制台中收到消息:“耗时:103 毫秒”,但这不是主要问题。)

Future<String> longComputation() async
{
  int startTime = DateTime.now().millisecondsSinceEpoch;

  String result = await Future.delayed(
    Duration(milliseconds: 100),
    () => "Fake computation result"
  );

  print("Time took: ${DateTime.now().millisecondsSinceEpoch - startTime} ms");
  return result;
}

我假设我在 longComputation 中搞砸了一些东西,但是什么?没有 未等待的期货

这种行为可能令人困惑,但重要的是要记住您的 Dart 代码仅在单个线程中执行(除非您使用隔离)。

问题是 .timeout 背后的逻辑需要在同一个单线程中 运行 而 Dart 不能停止执行您自己的代码。因此,如果您正在 运行 进行 CPU 密集计算而没有任何暂停,那么您将停止 Dart VM 运行 处理事件队列中的任何其他事件。

.timeout 的实现实际做的是创建一个内部 Timer,它将在未来被触发,除非您在超时值之前得到结果。这个 Timer 事件像 Dart VM 中的任何其他事件一样排在事件队列的顶部。

但在您的第一个示例中,我们实际上永远不会在您给出结果之前执行事件队列中的任何其他事件。因此,从 Future 的角度来看,您在截止日期前返回了结果。

这看起来 .timeout 有点毫无意义,但它的真正用途是当您进行一些 IO 操作时,例如 API 请求,而 Dart VM 实际上正在等待一些回答。

如果你打算用它进行繁重的计算,你可以生成一个 Isolate 这样你的主隔离实例可以在另一个隔离上等待。或者,您可以在计算中插入一些暂停,使 Dart VM space 执行事件队列中的其他事件。一个例子可能是插入 await Future<void>(() => null); ,这将在事件队列的顶部产生一个新事件。当我们等待队列上的所有事件都执行完毕后,再进行自己的空计算。

然后添加一些逻辑也很有意义,这样您自己的代码就可以查看是否已达到超时值,这样您就可以在达到超时值时停止计算。

您实际上无法取消 Future。充其量你可以让你的 .timeout 回调设置一个标志,longComputation 定期检查以尽快成为空操作。

另见 Future.timeout documention is misleading