2019 年春节前最后一更了
在 dart 中有生成器函数的语法,在很多其他的语言中也有,比如 js c#
这个语法看上去和 async
await
语法很像
使用的关键字是 async*
sync*
yield
yield*
官方对于这个语法的说明可以参考这个连接generators
其实async
await
也是一种生成器语法
生成器语法就是你返回的类型通常情况下和 return 的类型可能不一致
比如你return 1
,但是返回值上却需要写Future
在 dart 中可以使用这个便利的生成一个迭代器
如下所示
这两种写法是一样的,但是第一个写法会简洁很多
main(List arguments) {
print(genList());
print(genList2());
}
Iterable genList({int max = 10}) sync* {
var i = 0;
while (i < max) {
yield i;
i++;
}
}
Iterable genList2({int max = 10}) {
var list = [];
var i = 0;
while (i < max) {
list.add(i);
i++;
}
return list.map((i) => i);
}
这个返回值是一个 Stream
main(List arguments) {
print(genList());
print(genList2());
genStream().listen((data) {
print("stream1 : $data");
});
genStream2().listen((data) {
print("stream2 : $data");
});
}
Stream genStream({int max = 10}) async* {
int i = 0;
while (i < max) {
yield i;
await Future.delayed(Duration(milliseconds: 300));
i++;
}
}
Stream genStream2({int max = 10}) {
StreamController controller = StreamController();
Future.delayed(Duration.zero).then((_) async {
int i = 0;
while (i < max) {
controller.add(i);
await Future.delayed(Duration(milliseconds: 300));
i++;
}
controller.close();
});
return controller.stream;
}
两种写法达到了一样的效果,但是生成器函数代码会更加简洁一些
在生成器函数中还有一个关键字 yield*
这个关键字是结合递归使用的,可以配合sync*
也可以配合async*
main(List arguments) {
var r = naturalsDownFrom(10);
print(r); //(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
r = naturalsDownWithNormal(10);
print(r); //(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
}
Iterable naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
Iterable naturalsDownWithNormal(int n) {
var list = [];
if (n > 0) {
list.add(n);
var r = naturalsDownWithNormal(n - 1);
list.addAll(r);
}
return list.map((v) => v);
}
main(List arguments){
naturalsStreamDownFrom(10).listen((data) {
print("data = $data");
});
}
Stream naturalsStreamDownFrom(int n) async* {
if (n > 0) {
yield n;
yield* naturalsStreamDownFrom(n - 1);
}
}
输出结果
data = 10
data = 9
data = 8
data = 7
data = 6
data = 5
data = 4
data = 3
data = 2
data = 1
常规写法分开写
main(List arguments) {
naturalsStreamDownWithNormal(10).listen((data) {
print("data2 = $data");
});
}
Stream naturalsStreamDownWithNormal(int n) {
var controller = StreamController();
if (n > 0) {
controller.add(n);
naturalsStreamDownWithNormal(n - 1).listen((data) {
controller.add(data);
});
}
return controller.stream;
}
data2 = 10
data2 = 9
data2 = 8
data2 = 7
data2 = 6
data2 = 5
data2 = 4
data2 = 3
data2 = 2
data2 = 1
这里常规的写法也比较复杂,而且还有 controller 不关闭的可能
还需要注意一下 streamController 的关闭
需要修改如下
Stream naturalsStreamDownWithNormal(int n) {
var controller = StreamController();
if (n > 0) {
controller.add(n);
naturalsStreamDownWithNormal(n - 1).listen((data) {
controller.add(data);
}, onDone: () {
print("close controller = $n");
controller.close();
});
} else {
controller.close();
}
return controller.stream;
}
这里加了一个 print 输出
close controller = 1
data2 = 10
close controller = 2
data2 = 9
close controller = 3
data2 = 8
close controller = 4
data2 = 7
close controller = 5
data2 = 6
close controller = 6
data2 = 5
close controller = 7
data2 = 4
close controller = 8
data2 = 3
close controller = 9
data2 = 2
close controller = 10
data2 = 1
日志是这样的,递归调用,结束后递归关闭
官方的说法是,使用yield*会有性能优化,所以还是建议使用生成器函数
粗略的分析了一下生成器函数,记录下,为了以后的朋友能看到,同时最重要的是记录自己的学习过程
以上