dart中的生成器函数

文章目录

2019 年春节前最后一更了

在 dart 中有生成器函数的语法,在很多其他的语言中也有,比如 js c#

这个语法看上去和 async await 语法很像

使用的关键字是 async* sync* yield yield*

官方对于这个语法的说明可以参考这个连接

其实async await也是一种生成器语法

生成器语法就是你返回的类型通常情况下和 return 的类型可能不一致

比如你return 1,但是返回值上却需要写Future<int>

sync*

在 dart 中可以使用这个便利的生成一个迭代器

如下所示

20190203173018.png

这两种写法是一样的,但是第一个写法会简洁很多

 1main(List<String> arguments) {
 2  print(genList());
 3  print(genList2());
 4}
 5
 6Iterable<int> genList({int max = 10}) sync* {
 7  var i = 0;
 8  while (i < max) {
 9    yield i;
10    i++;
11  }
12}
13
14Iterable<int> genList2({int max = 10}) {
15  var list = <int>[];
16  var i = 0;
17  while (i < max) {
18    list.add(i);
19    i++;
20  }
21  return list.map((i) => i);
22}

async*

这个返回值是一个 Stream

 1main(List<String> arguments) {
 2  print(genList());
 3  print(genList2());
 4
 5  genStream().listen((data) {
 6    print("stream1 : $data");
 7  });
 8
 9  genStream2().listen((data) {
10    print("stream2 : $data");
11  });
12}
13
14
15Stream<int> genStream({int max = 10}) async* {
16  int i = 0;
17  while (i < max) {
18    yield i;
19    await Future.delayed(Duration(milliseconds: 300));
20    i++;
21  }
22}
23
24Stream<int> genStream2({int max = 10}) {
25  StreamController<int> controller = StreamController();
26
27  Future<void>.delayed(Duration.zero).then((_) async {
28    int i = 0;
29    while (i < max) {
30      controller.add(i);
31      await Future.delayed(Duration(milliseconds: 300));
32      i++;
33    }
34    controller.close();
35  });
36
37  return controller.stream;
38}

两种写法达到了一样的效果,但是生成器函数代码会更加简洁一些

执行结果如下

20190203174030.png

yield*

在生成器函数中还有一个关键字 yield*

这个关键字是结合递归使用的,可以配合sync* 也可以配合async*

结合 sync*

 1main(List<String> arguments) {
 2  var r = naturalsDownFrom(10);
 3  print(r); //(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
 4
 5  r = naturalsDownWithNormal(10);
 6  print(r); //(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
 7}
 8
 9Iterable<int> naturalsDownFrom(int n) sync* {
10  if (n > 0) {
11    yield n;
12    yield* naturalsDownFrom(n - 1);
13  }
14}
15
16Iterable<int> naturalsDownWithNormal(int n) {
17  var list = <int>[];
18  if (n > 0) {
19    list.add(n);
20    var r = naturalsDownWithNormal(n - 1);
21    list.addAll(r);
22  }
23  return list.map((v) => v);
24}

结合 async*

 1
 2main(List<String> arguments){
 3  naturalsStreamDownFrom(10).listen((data) {
 4    print("data = $data");
 5  });
 6
 7}
 8
 9Stream<int> naturalsStreamDownFrom(int n) async* {
10  if (n > 0) {
11    yield n;
12    yield* naturalsStreamDownFrom(n - 1);
13  }
14}

输出结果

 1data = 10
 2data = 9
 3data = 8
 4data = 7
 5data = 6
 6data = 5
 7data = 4
 8data = 3
 9data = 2
10data = 1

常规写法分开写

 1main(List<String> arguments) {
 2  naturalsStreamDownWithNormal(10).listen((data) {
 3    print("data2  = $data");
 4  });
 5}
 6
 7
 8Stream<int> naturalsStreamDownWithNormal(int n) {
 9  var controller = StreamController<int>();
10  if (n > 0) {
11    controller.add(n);
12    naturalsStreamDownWithNormal(n - 1).listen((data) {
13      controller.add(data);
14    });
15  }
16  return controller.stream;
17}
 1data2  = 10
 2data2  = 9
 3data2  = 8
 4data2  = 7
 5data2  = 6
 6data2  = 5
 7data2  = 4
 8data2  = 3
 9data2  = 2
10data2  = 1

这里常规的写法也比较复杂,而且还有 controller 不关闭的可能

还需要注意一下 streamController 的关闭

需要修改如下

 1
 2Stream<int> naturalsStreamDownWithNormal(int n) {
 3  var controller = StreamController<int>();
 4  if (n > 0) {
 5    controller.add(n);
 6    naturalsStreamDownWithNormal(n - 1).listen((data) {
 7      controller.add(data);
 8    }, onDone: () {
 9      print("close controller = $n");
10      controller.close();
11    });
12  } else {
13    controller.close();
14  }
15  return controller.stream;
16}

这里加了一个 print 输出

 1close controller = 1
 2data2  = 10
 3close controller = 2
 4data2  = 9
 5close controller = 3
 6data2  = 8
 7close controller = 4
 8data2  = 7
 9close controller = 5
10data2  = 6
11close controller = 6
12data2  = 5
13close controller = 7
14data2  = 4
15close controller = 8
16data2  = 3
17close controller = 9
18data2  = 2
19close controller = 10
20data2  = 1

日志是这样的,递归调用,结束后递归关闭

官方的说法是,使用yield*会有性能优化,所以还是建议使用生成器函数

后记

粗略的分析了一下生成器函数,记录下,为了以后的朋友能看到,同时最重要的是记录自己的学习过程

以上