Dart 中等待和监听的区别

Difference between await for and listen in Dart

我正在尝试创建网络服务器流。这是代码:

import 'dart:io';

main() async {
  HttpServer requestServer = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 8000);

requestServer.listen((request) {   //comment out this or the await for to work
  request.response
    ..write("This is a listen stream")
    ..close();
});

  await for (HttpRequest request in requestServer) {
  request.response
    ..write("This is an await for stream")
    ..close();
  }
}

监听和等待有什么区别?它们不能同时工作。您需要注释掉一个或另一个才能工作,但这里的功能似乎没有区别。在某些情况下是否存在差异,什么时候应该使用一个而不是另一个?

鉴于:

Stream<String> stream = new Stream<String>.fromIterable(['mene', 'mene', 'tekel', 'parsin']);

然后:

print('BEFORE');
stream.listen((s) { print(s); });
print('AFTER');

产量:

BEFORE
AFTER
mene
mene
tekel
parsin

鉴于:

print('BEFORE');
await for(String s in stream) { print(s); }
print('AFTER');

产量:

BEFORE
mene
mene
tekel
parsin
AFTER

stream.listen()设置代码,当事件到达时将其放入事件队列,然后执行以下代码。

await for 在事件之间挂起并一直这样做 直到流完成 ,因此在完成之前不会执行后面的代码。

我使用 `await 是因为我知道我有一个流会有有限的事件,我需要在做任何其他事情之前处理它们(基本上就像我正在处理一个期货列表)。

检查 https://www.dartlang.org/articles/language/beyond-async 以获得对 await for 的描述。

主要区别在于之后有代码。 listen 仅注册处理程序并继续执行。 await for 将保持执行直到流关闭。

因此,如果您在 main 的末尾添加一个 print('hello');,您不应该在带有 await for 的输出中看到 hello (因为请求流永远不会关闭)。 Try the following code on dartpad 查看差异:

import 'dart:async';
main() async {
  tenInts.listen((i) => print('int $i'));
  //await for (final i in tenInts) {
  //  print('int $i');
  //}
  print('hello');
}
Stream<int> get tenInts async* {
  for (int i = 1; i <= 10; i++) yield i;
}

一个更重要的区别是 await for 序列化流项目的消费,而 listen 将同时处理它们。

例如下面的代码:

import 'dart:async';

Future<void> process(int i) async {
  print("start $i");
  await new Future.delayed(const Duration(seconds: 1));
  print("end $i");
}

main() async {
  await for (final i in tenInts) {
    await process(i);
  }
  tenInts.listen((i) async => await process(i));
  print('hello');
}
Stream<int> get tenInts async* {
  for (int i = 1; i <= 10; i++) yield i;
}

产量

start 1
end 1
start 2
end 2
start 3
end 3
start 4
end 4
start 5
end 5
start 6
end 6
start 7
end 7
start 8
end 8
start 9
end 9
start 10
end 10
hello
start 1
start 2
start 3
start 4
start 5
start 6
start 7
start 8
start 9
start 10
end 1
end 2
end 3
end 4
end 5
end 6
end 7
end 8
end 9
end 10

另一个区别是 listen() returns 您的 StreamSubscription 对象,可以在以后的任何时间点用于 cancel/pause 订阅。您可以设置为每个数据事件或错误事件以及流关闭时调用的回调。

下面演示的是听流5秒后取消。

Stream<int> gen() async* {
  for (int i = 1; i <= 10; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
  print("done");
}

main() async {
  Stream<int> stream = gen();
  var subscription = stream.listen((item){
    print(item);
  });
  
  await Future.delayed(Duration(seconds: 5));
  subscription.cancel();
  
  print("Exit");
}

Output:
1
2
3
4
Exit

正如罗布森所说:

await for serializes the consumption of the stream items while listen will process them concurrently.

我还想补充一点,如果使用 pause 和 resume 方法,虽然使用 listen 方法可以逐个处理流事件。 暂停方法应该在第一个 await 关键字之前调用。

StreamSubscription<int> subscription;
subscription = tenInts.listen((i) async {
    subscription.pause();
    await process(i);
    subscription.resume();
  });

Future<void> process(int i) async {
  print("start $i");
  await new Future.delayed(const Duration(seconds: 1));
  print("end $i");
}