我想知道这两者之间的区别。在javascript方面,我找到了这个Stack Overflow的帖子,Generator函数中的被委派的yield(yield star,yield *)
据我所知,yield*
委托给另一个生成器,在另一个生成器停止产生值后,它会继续生成自己的值。
对dart方面的解释和示例将会很有帮助。
我想知道这两者之间的区别。在javascript方面,我找到了这个Stack Overflow的帖子,Generator函数中的被委派的yield(yield star,yield *)
据我所知,yield*
委托给另一个生成器,在另一个生成器停止产生值后,它会继续生成自己的值。
对dart方面的解释和示例将会很有帮助。
yield
它用于从生成器中发出值,可以是异步或同步的。
示例:
Stream<int> getStream(int n) async* {
for (var i = 1; i <= n; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() {
getStream(3).forEach(print);
}
输出:
1
2
3
yield*
它将调用委托给另一个生成器,并在该生成器停止产生值后,恢复生成自己的值。
示例:
Stream<int> getStream(int n) async* {
if (n > 0) {
await Future.delayed(Duration(seconds: 1));
yield n;
yield* getStream(n - 1);
}
}
void main() {
getStream(3).forEach(print);
}
输出:
3
2
1
yield
在可迭代对象或流中提供单个值。yield*
从另一个可迭代对象或流中提供多个值。看一下以下两个可迭代对象是如何交互的:
void main() {
final myIterable = getValues();
for (int value in myIterable) {
print(value);
}
}
Iterable<int> getValues() sync* {
yield 42;
yield* getThree();
yield* [6, 7, 8];
yield 24;
}
Iterable<int> getThree() sync* {
yield 1;
yield 2;
yield 3;
}
42
1
2
3
6
7
8
24
现在让我们来看看流版本:
Future<void> main() async {
final myStream = getValues();
await for (int value in myStream) {
print(value);
}
}
Stream<int> getValues() async* {
yield 42;
yield* getThree();
yield* Stream.fromIterable([6, 7, 8]);
yield 24;
}
Stream<int> getThree() async* {
yield 1;
yield 2;
yield 3;
}
注意:
async*
创建一个流,使其成为一个异步生成器函数。yield
提供一个单一的值。yield*
从另一个流中提供多个值。yield*
在继续之前等待被调用的流完成。运行它,你将再次看到以下输出:
42
1
2
3
6
7
8
24
yield*
的含义产生了严重的误解,即使在观看了Generator Functions - Flutter in Focus视频之后。感谢@Hackmodford在评论中纠正了我的错误。await
在async*
情况下的使用方式开始,但在sync*
情况下却不能使用。因此我推测yield*
只能在sync*
生成器中使用,如果周围的函数是一个sync*
生成器的话,而yield*
可以在async*
或sync*
生成器中使用,在async*
生成器内部。另外可以指出的一件事是,*
只是表示生成器
,没有其他含义,而sync*
需要明确指定的唯一原因是在函数后面只加上*
不是一个好的语法。 - undefined我创建了一个Dart Pad链接,以帮助人们进行实验:
Yield*用于一次返回整个可迭代对象一个值,而无需使用循环。
这两个函数完全相同,基于开始和结束值生成一个可迭代对象。
Iterable<int> getRangeIteration(int start, int finish) sync* {
for(int i = start; i<= finish; i++){
yield i;
}
}
Iterable<int> getRangeRecursive(int start, int finish) sync* {
if (start <= finish) {
yield start;
yield* getRangeRecursive(start + 1, finish);
}
}
yield* getRangeRecursive(start + 1, finish);
如果我们这样做
yield getRangeRecursive(start + 1, finish);
我们将会得到一个编译器错误:
The type 'Iterable<Iterable<int>>' implied by the 'yield' expression must be assignable to 'Iterable<int>'.
正如您所看到的,yield将可迭代对象包装在另一个Iterable<>中,使其类型为Iterable<Iterable>。这与函数的返回类型不匹配。
如果我们必须在不使用yield*的情况下进行递归,我们需要这样做:
Iterable<int> getRangeRecursiveWithOutYieldStar(int start, int finish) sync* {
if (start <= finish) {
yield start;
for (final val in getRangeRecursiveWithOutYieldStar(start + 1, finish)){
yield val;
}
}
}
这样做会变得混乱且效率低下。
因此,在我看来,yield* 会展开另一个生成器函数。
一些好的资源: Flutter in Focus视频 - 生成器函数
中等大小的文章: Dart中的sync*、async*、yield和yield*是什么?
我花了一些时间才弄明白,但这是我目前的理解:
*
的意义*
只是表示“生成器”。所以:
sync*
是一个同步生成器函数(一个可以yield
值但不能await
异步函数调用的函数)。sync*
函数的类型是Iterable<T>
。async*
是一个异步生成器函数(一个可以yield
值的函数,包括在await
另一个async
函数的函数调用结果之后)。async*
函数的类型是Stream<T>
,在概念上有点像“异步可迭代对象”,尽管Stream
和Iterable
有不同的API,如下所述。yield
关键字产生值的函数)在每个值被yield
后,其执行状态会被冻结,直到调用者请求下一个值之前,生成器函数不会执行任何其他代码。这个过程会一直持续,直到生成器函数返回,此时调用者会被告知没有更多的值可用。
for
与Iterable
(sync*
)一起使用,await for
与Stream
(async*
)一起使用假设有以下函数:
Iterable<T> syncIterable() sync* {...}
和Stream<T> asyncStream() async* {...}
,如果你想遍历这些生成器函数产生的结果:
sync*
生成器函数,你需要使用for (var x in syncIterable)
。async*
生成器函数,你需要使用await for (var y in asyncStream)
。如果你尝试使用错误类型的生成器,会出现错误:
for (var x in asyncStream)
,你会得到错误:在'for'循环中使用的类型Stream<T>必须实现'Iterable'
。这样做的目的是禁止同步代码调用异步代码。同步代码无法跨越异步间隙。await for (var x in syncIterable)
,你会得到错误:在'for'循环中使用的类型Iterable<T>必须实现'Stream'
。异步代码总是可以调用同步代码,但这样做的目的是强调同步生成器在概念上不会将产生值的代码包装在Future<T>
中(因此这是一个类型错误)。Iterable
和Stream
之间的区别for
和await for
之外,处理Iterable
和Stream
还有其他方法。例如,对于Iterable
,您可以调用syncIterable.iterator
来获取一个Iterator
,然后通过调用current
和moveNext()
来逐个处理值,而不是使用for
循环。Stream
没有包含.iterator
属性,因为Iterator
API是同步的,而Stream
是异步的。提供.iterator
将允许同步代码调用异步代码而无需await
。简单来说,无法将异步API(在这里是Stream
)包装在同步包装器(在这里是Iterator
)中。再次强调,同步代码无法跨越异步间隙。
yield*
的含义如果你尝试在错误类型的生成器中使用yield*
,你会得到一个错误:
sync*
函数内部调用yield* asyncStream
,你会得到一个错误:由'yield*'表达式暗示的类型'Stream<T>'必须可分配给'Iterable<T>'
。async*
函数内部调用yield* syncIterable
,你会得到一个错误:由'yield*'表达式暗示的类型'Iterable<T>'必须可分配给'Stream<T>'
。Iterable<int> getRangeYield(int start, int end) sync* {
if (start <= end) {
yield start;
for (final int val in getRangeYield(start + 1, end)) {
yield val;
}
}
}
Iterable<int> getRangeYieldAnalysed(int start, int end) sync* {
if (start <= end) {
print('before start $start');
yield start * 10;
print('after start $start');
for (final int val in getRangeYieldAnalysed(start + 1, end)) {
print('before val $val');
yield val * 100;
print('after val $val');
}
}
}
Iterable<int> getRangeYieldStar(int start, int end) sync* {
// same output as getRangeYield()
if (start < end) {
yield* getRangeYieldStar(start + 1, end);
}
yield start;
}
Iterable<int> getRangeYieldStarAnalysed(int start, int end) sync* {
// same output as getRangeYield()
print('generator $start started');
if (start < end) {
yield* getRangeYieldStarAnalysed(start + 1, end);
}
yield start;
print('generator $start ended');
}
Iterable<int> getRangeForLoop(int start, int end) sync* {
// same output as getRangeYield()
for (int i = start; i <= end; i++) {
yield i;
}
}
void main() {
Iterable<int> it = getRangeYieldStarAnalysed(1, 4);
print('main range obtained');
for(int element in it) {
print('el $element');
};
}
yield numbersDownFrom(n - 1)
而不是yield* numbersDownFrom(n - 1);
,会发生什么? - sonicyield*
用于将调用委托给生成器,因为yield numbersDownFrom(n-1)
不会有任何意义,因为yield
只会产生您在Stream
中想要返回的类型。 - CopsOnRoad