Dart中async和async*有什么区别?

212

我正在使用Flutter框架开发一个应用程序。 期间,我遇到了Dart中的asyncasync*关键字。 有人能告诉我它们之间的区别吗?


1
是的,来自谷歌极客们的超级神奇语义。 - Georgiy Chebotarev
5个回答

293

简短回答

  • async 关键字会给你一个 Future
  • async* 关键字会给你一个 Stream

async

你可以在某个需要执行一些可能需要花费很长时间的工作的函数前加上 async 关键字。它会返回被包装在 Future 中的结果。

Future<int> doSomeLongTask() async {
  await Future.delayed(const Duration(seconds: 1));
  return 42;
}

你可以通过等待Future来获取那个结果:

main() async {
  int result = await doSomeLongTask();
  print(result); // prints '42' after waiting 1 second
}

异步*

您可以添加async*关键字来创建一个返回一系列未来值的函数。结果将被包装在一个Stream中。

Stream<int> countForOneMinute() async* {
  for (int i = 1; i <= 60; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
}

这个技术术语的称呼是异步生成器函数。你使用yield来返回值,而不是return,因为你没有离开函数。
你可以使用await for等待Stream发出的每个值。
main() async {
  await for (int i in countForOneMinute()) {
    print(i); // prints 1 to 60, one integer per second
  }
}

进行中

观看这些视频以了解更多信息,特别是生成器的视频:


7
请看这些视频。如果可以提供文本参考资料,那就太好了。并非每个人都喜欢视频... - Aconcagua
6
非常完美的解释。我印象深刻。 - dontknowhy
1
好的解释。 - Sahan Monaara
2
如果异步生成函数(async*)没有yield却返回一个值会发生什么? - kent2508
3
Dart不允许从async*函数中返回一个值,只能使用yield来产生值。但是,你可以调用return;(没有返回值)来提前退出该函数。 - Suragch
显示剩余2条评论

246

将函数标记为asyncasync*,允许其使用Futureasync/await

两者之间的区别是,async*将始终返回一个Stream,并通过yield关键字提供一些语法糖来发出值。

因此,我们可以执行以下操作:

Stream<int> foo() async* {
  for (int i = 0; i < 42; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
}

这个函数每秒钟会发出一个值,并且每次都会增加。


18
通过将函数标记为async*,我们可以使用yield关键字并返回一个数据流(Stream)。 - Yash

20

解决方案、起源和洞见

本答案包含简化和易于理解的例子

异步

异步计算无法在启动时立即提供结果,因为程序可能需要等待外部响应,例如:

  • 读取文件
  • 查询数据库
  • 从API获取数据

异步计算不会阻止所有计算直到结果可用,而是立即返回一个Future对象,该对象最终将“完成”并得到结果。

示例(此类型的异步调用仅可在没有返回响应的情况下使用):

void main() async {
  // The next line awaits 5 seconds
  await Future.delayed(Duration(seconds: 5));
  // Pseudo API call that takes some time
  await fetchStocks();
}

Future

Future代表一个不能立即完成的计算。普通函数返回结果,而异步函数返回Future,该Future最终将包含结果。Future会告诉您何时准备好结果。

  • 当异步函数返回值时,将附加Future
  • 表示单个计算的结果(与Stream相对)

例如:

Future<String> fetchUserOrder() =>
    // Imagine that this function is more complex and slow.
  Future.delayed(
    const Duration(seconds: 2),
        () => 'Large Latte',
  );

void main(List<String> arguments) async {
  var order = await fetchUserOrder(); 
  // App awaits 2 seconds
  print('Your $order is ready');
}

Stream

Stream是一种异步数据事件源,提供了接收事件序列的方式。每个事件都可以是数据事件,也称为流中的元素。

  • Stream是一系列结果的序列
  • 从Stream中您会收到通知以获取结果(流元素)

async* (streams)

async*是一个异步生成器,它返回一个Stream对象,用于创建流。

使用流和async*的示例:

// Creating a new stream with async*
// Each iteration, this stream yields a number
Stream<int> createNumberStream(int number) async* {
  for (int i = 1; i <= number; i++) {
    yield i;
  }
}

void main(List<String> arguments) {
  // Calling the stream generation
  var stream = createNumberStream(5);
  // Listening to Stream yielding each number
  stream.listen((s) => print(s));
}

结果:

1
2
3
4
5

奖励:转换现有流

如果您已经有一个流,您可以基于原始流的事件将其转换为新流。

示例(与之前相同的代码但稍有不同):

Stream<int> createNumberStream(int number) async* {
  for (int i = 1; i <= number; i++) {
    yield i;
  }
}

// This part is taking a previous stream through itself and outputs updated values
// This code multiplies each number from the stream
Stream<int> createNumberDoubling(Stream<int> chunk) async* {
  await for (final number in chunk) {
    yield number*2;
  }
}

void main(List<String> arguments) {
  // Here we are Transforming the first stream through createNumberDoubling stream generator
  var stream = createNumberDoubling(createNumberStream(5));
  stream.listen((s) => print(s));
}

结果:

2
4
6
8
10

解决方案

asyncasync* 是密切相关的,它们甚至来自同一个库 dart:asyncasync 表示一个 Future 和一次性交换,而 async* 则表示一个 Stream,即多个事件的流。


4

异步函数在遇到await关键字之前是同步执行的。因此,异步函数体内的所有同步代码都会立即执行。

Future<int> foo() async {
  await Future.delayed(Duration(seconds: 1));
  return 0;
}

Async*被用来创建一个函数,它可以逐个返回一组未来的值。每个结果都被包装在流(Stream)中。

Stream<int> foo() async* {
  for (var i = 0; i < 10; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }

}


2

async* 会始终返回一个 Stream

Stream<int> mainStream(int value) async* {
  for (int i = 1; i <= value; i++) {
    yield i;
  }
}

async将结果包装在Future中返回,因此可能需要更长的时间。请参考下面的示例:

void main() async {
  // The next line awaits 10 seconds
  await Future.delayed(Duration(seconds: 10));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接