Dart:生成器

  1. 生成器的作用
  2. 同步生成器 sync*
  3. 异步生成器 async*
  4. yield*
    1. 基本使用
    2. 递归

生成器的作用

在 Dart 里,生成器(generator)是一种分批返回数据的方法,它允许你用 yield 语句一次返回一个值,而不是一次性返回整个集合。

  • 普通函数:一次性返回结果
  • 生成器函数:可以暂停 → 返回一个值 → 再继续执行 → 再返回

好处:

  • 节省内存(不需要一次构造全部数据)
  • 可以动态生成序列(比如无限序列)
  • 可中断和恢复执行

同步生成器 sync*

适用于 同步 地生成多个值,返回类型是 Iterable<T>

  • sync* 声明
  • yield 返回单个值
  • 返回类型必须是 Iterable<T>
  • 遍历时是同步的(阻塞执行)
// sync*:用于定义同步生成器函数,返回一个 Iterable。
// sync* 函数可以使用 yield 关键字来逐个返回值,
// 注意:sync* 函数是惰性求值的,只有在迭代时才会执行
Iterable<int> mySyncGenerator() sync* {
  print("********************************");
  print("处理业务逻辑:1");
  yield 1; // 第一次返回 1
  print("处理业务逻辑:2");
  yield 2; // 第二次返回 2
  print("处理业务逻辑:3");
  yield 3; // 第三次返回 3
  print("********************************");
}

void main() {
  print("main 开始执行");
  // mySyncGenerator() 返回一个 Iterable<int>
  // 使用 for-in 循环来迭代这个 Iterable
  // 每次迭代都会调用 mySyncGenerator() 中的 yield
  for (var value in mySyncGenerator()) {
    print("--------------");
    print("获得数据:$value");
    print("--------------");
  }
  print("main 执行完毕");
}

输出结果:

main 开始执行
********************************
处理业务逻辑:1
--------------
获得数据:1
--------------
处理业务逻辑:2
--------------
获得数据:2
--------------
处理业务逻辑:3
--------------
获得数据:3
--------------
********************************
main 执行完毕

生成器可以传入参数:

Iterable<int> naturalsTo(int n) sync* {
  print('start');
  int k = 0;
  while (k < n) {
    yield k++;
  }
  print('end');
}

main(List<String> args) {
  var it = naturalsTo(5).iterator;
  while (it.moveNext()) {
    print(it.current);
  }
}
/*
start
0
1
2
3
4
end
*/

异步生成器 async*

适用于 异步 地生成多个值,返回类型是 Stream<T>

特点

  • async* 声明
  • yield 返回单个值(可能有异步延迟)
  • 返回类型必须是 Stream<T>
  • 遍历时用 await for或者监听器

适用场景:需要逐步发送数据(比如网络请求返回一批数据、WebSocket 消息等)

更多内容可以查看异步---> Stream

Stream<int> myAsyncGenerator() async* {
  yield 1;
  await Future.delayed(Duration(seconds: 1));
  yield 2;
  await Future.delayed(Duration(seconds: 1));
  yield 3;
}

void main() async {
  await for (var value in myAsyncGenerator()) {
    print(value);
  }
}

void main() {
  print("开始接收数据");
  // 监听 countStream() 返回的 Stream<int>
  // 使用 listen 方法来处理 Stream 中的数据
  myAsyncGenerator().listen((value) {
    print('收到: $value');
  });
  print("数据接收完毕");
}

yield*

基本使用

yield* 用于 委托 给另一个可迭代对象或生成器,相当于**“把对方的 yield 全都接管过来”**。

好处

  • 避免手动一条条 yield 转发
  • 可以实现递归生成(特别是树遍历、目录遍历等)
Stream<int> oneToThreeAsync() async* {
  yield 1;
  yield 2;
  yield 3;
}

Stream<int> oneToSixAsync() async* {
  // 使用 yield* 来包含另一个 Stream
  // yield* oneToThreeAsync() 会将 oneToThreeAsync() 的所有值都发送到当前 Stream
  yield* oneToThreeAsync();
  yield 4;
  yield 5;
  yield 6;
}

void main() {
  oneToSixAsync().listen((value) {
    print(value);
  });
}

递归

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

main(List<String> args) {
  var it = naturalsDownFrom(5).iterator;
  while (it.moveNext()) {
    print(it.current);
  }
}
// 输出:
// 5
// 4
// 3
// 2
// 1
  1. 调用主函数
    • 执行 naturalsDownFrom(5)
      → 返回一个 Iterable<int> 对象(未执行函数体)
  2. 获取迭代器
    • var it = naturalsDownFrom(5).iterator
  3. 第一次 moveNext()
    • 进入 naturalsDownFrom(5)
      • n > 0yield 5 → 暂停 → 返回 true
    • current = 5 → 打印 5
  4. 第二次 moveNext()
    • 从上次 yield 5 后继续执行
    • 遇到 yield* naturalsDownFrom(4)
      → 进入 naturalsDownFrom(4)
      • n > 0yield 4 → 暂停 → 返回 true
    • current = 4 → 打印 4
  5. 第三次 moveNext()
    • 继续 naturalsDownFrom(4) 执行
    • yield* naturalsDownFrom(3)
      • n > 0yield 3 → 暂停 → 返回 true
    • current = 3 → 打印 3
  6. 第四次 moveNext()
    • 继续 naturalsDownFrom(3)
    • yield* naturalsDownFrom(2)
      • n > 0yield 2 → 暂停 → 返回 true
    • current = 2 → 打印 2
  7. 第五次 moveNext()
    • 继续 naturalsDownFrom(2)
    • yield* naturalsDownFrom(1)
      • n > 0yield 1 → 暂停 → 返回 true
    • current = 1 → 打印 1
  8. 第六次 moveNext()
    • 继续 naturalsDownFrom(1)
    • yield* naturalsDownFrom(0)
      • n > 0 不成立 → 递归结束
    • 所有递归返回,moveNext() 返回 false
    • 循环结束

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com

×

喜欢就点赞,疼爱就打赏