无限递归 dart 生成器 (yield*) 堆栈会溢出吗?

Can an infinite recursive dart generator(yield*) stack overflow?

我想知道无限递归生成器是否会导致堆栈溢出错误。我不确定无限递归是否是生成器的正确用例,但我在文档中找不到任何关于它的线索。

举个例子:

import 'dart:async';

Stream<bool> infiniteStream(bool t) async* {
  yield t;
  await Future.delayed(Duration(milliseconds: 1));
  yield* infiniteStream(!t);
}

void main() async {
  await for (final value in infiniteStream(true)) {
    print(value);
  }
}

这是对生成器的正确使用吗?还是时间长了会造成error/performance的问题? 如果这不正确,那么实现可以向自身添加事件以响应接收到的事件的无限流的正确方法是什么?

我还注意到如果没有 Future.delayed(),dartpad 会崩溃。

它肯定会溢出一些东西,但在这种情况下它很可能是堆,所以这不太可能很快发生。 (不过,如果让它保持 运行 足够长的时间,它就会发生。)

因为代码是异步的,所以实际的堆栈跟踪只是从事件循环到异步计算的继续。它不保留启动异步计算的堆栈(因为该堆栈已经有一个流或很久以前返回给它的未来)。 它可能必须通过递归深度线性的 Stream 听众链传播产生的值,但这些将是 heap-allocated.

这是基于当前的实施策略。没有什么可以阻止基于用于异步计算的单独堆栈的实现,它可以为 yield* 操作(或 await asyncFunction() 调用)重用相同的堆栈。如果发生这样的实现,将不再保证您不会看到此类代码的堆栈溢出。

类似地,sync* 函数如果进行无限递归 yield* 可能会导致堆栈溢出,但目前也仅使用堆 space.