Dart【06】generator生成器函数

207 阅读2分钟

什么是生成器函数

Dart生成器函数 (generator) 可以渐进的返回一个值的序列。

Dart内置了两种生成器函数的支持:

同步生成器(sync*):返回一个Iterable对象。 异步生成器(async*):返回一个Stream对象。

同步生成器

使用同步生成器修饰的函数返回的是迭代器Iterable

Iterable主要使用iterator的两个方法moveNext()currentcurrent代表了当前元素,moveNext()将迭代器推进到迭代的下一个元素。

用一段代码展示如何使用同步生成器:

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

测试代码:

void main() {
  Iterable<int> sequence = naturalsTo(5);
​
  print("Starting...");
​
  for (int value in sequence) {
    print(value);
  }
  print("DONE");
}

输出如下:

Starting... 0 1 2 3 4 DONE

代码解读:

上面示例中的naturalsTo函数就是使用同步生成器修饰的函数,首先在函数方法体花括号前加入sync*关键字,然后使用yield关键字进行值的返回。

上面的代码示例yield关键字向迭代器序列依次添加了五个值。

看到这里你可能有点疑问,函数返回值不是return吗?yield是干嘛的?

yield和yield*

yield的作用:

yield向周围sync*函数的迭代器中添加一个值。就像return,但不会终止函数。

上面的代码示例yield关键字向迭代器序列依次添加了五个值。

yield功能类似的还有一个关键字yield*yield*只是一种简化写法。如果生成器是递归的,可以使用 yield* 来提高其性能。

用两段代码示例来展示yield*是如何简化代码的:

这是一段没有使用yield*的代码:

void main() {
  for (int i in numbersDownFrom(3)) {
    print('$i');
  }
}
​
Iterable<int> numbersDownFrom(int n) sync* {
  if (n != 0) {
    yield n;
    for (var val in numbersDownFrom(n - 1)) {
      yield val;
    }
  }
}

输出如下:

3 2 1

 for (var val in numbersDownFrom(n - 1)) {
      yield val;
    }

这段代码是函数的递归调用,代码流程完全是同步的,你可能要花上一点时间阅读一下代码,你就能理解这段代码做了什么事情。

接下来我们用yield*来简化这段代码:

void main() {
  for (int i in numbersDownFrom(3)) {
    print('$i');
  }
}
​
Iterable<int> numbersDownFrom(int n) sync* {
  if (n != 0) {
    yield n;
    yield* numbersDownFrom(n - 1);
  }
}

输出如下:

3 2 1

两段代码的作用完全一样,你应该已经理解yield*的作用了,就是对For循环+yield的一种简化。

异步生成器

异步生成器async*在原理上和同步生成器完全一样,都是渐进的返回一个值的序列,唯一区别就是同步生成器返回的是迭代器。异步声明器返回的是Stream

关于Stream的详细介绍参考作者之前的文章。

用一段与上面sync*使用方式一样的代码来为你展示如何使用异步生成器async*

Stream<int> naturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

测试代码:

void main() async{
  Stream<int> sequence = naturalsTo(5);
​
  print("Starting...");
​
  await for (int value in sequence) {
    print(value);
  }
  print("DONE");
}

输出如下:

Starting... 0 1 2 3 4 DONE

async*sync*工作原理完全一致,如果你对asyncawait for有疑问,参考作者之前的文章。

sync,sync*,async,async*

文章看到这里对异步的全部介绍已经结束了,用一张图片来帮助你记忆sync,sync*,async,async*的区别。