生成器的作用
在 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
- 调用主函数
- 执行
naturalsDownFrom(5)
→ 返回一个Iterable<int>
对象(未执行函数体)
- 执行
- 获取迭代器
var it = naturalsDownFrom(5).iterator
- 第一次
moveNext()
- 进入
naturalsDownFrom(5)
n > 0
→yield 5
→ 暂停 → 返回true
current = 5
→ 打印5
- 进入
- 第二次
moveNext()
- 从上次
yield 5
后继续执行 - 遇到
yield* naturalsDownFrom(4)
→ 进入naturalsDownFrom(4)
n > 0
→yield 4
→ 暂停 → 返回true
current = 4
→ 打印4
- 从上次
- 第三次
moveNext()
- 继续
naturalsDownFrom(4)
执行 yield* naturalsDownFrom(3)
n > 0
→yield 3
→ 暂停 → 返回true
current = 3
→ 打印3
- 继续
- 第四次
moveNext()
- 继续
naturalsDownFrom(3)
yield* naturalsDownFrom(2)
n > 0
→yield 2
→ 暂停 → 返回true
current = 2
→ 打印2
- 继续
- 第五次
moveNext()
- 继续
naturalsDownFrom(2)
yield* naturalsDownFrom(1)
n > 0
→yield 1
→ 暂停 → 返回true
current = 1
→ 打印1
- 继续
- 第六次
moveNext()
- 继续
naturalsDownFrom(1)
yield* naturalsDownFrom(0)
n > 0
不成立 → 递归结束
- 所有递归返回,
moveNext()
返回false
- 循环结束
- 继续
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com