Difference between await for and listen in Dart
Asked Answered
H

5

40

I am trying to create a web server stream. Here is the code:

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();
  }
}

What is the difference between listen and await for? They both do not work at the same time. You need to comment out one or the other to work, but there doesn't seem to be a difference in function here. Are there circumstances where there is a difference, and when should you use one over the other?

Hexagon answered 5/3, 2017 at 17:54 Comment(0)
T
80

Given:

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

then:

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

yields:

BEFORE
AFTER
mene
mene
tekel
parsin

whereas:

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

yields:

BEFORE
mene
mene
tekel
parsin
AFTER

stream.listen() sets up code that will be put on the event queue when an event arrives, then following code is executed.

await for suspends between events and keeps doing so until the stream is done, so code following it will not be executed until that happens.

I use `await for when I have a stream that I know will have finite events, and I need to process them before doing anything else (essentially as if I'm dealing with a list of futures).

Check https://www.dartlang.org/articles/language/beyond-async for a description of await for.

Thereinto answered 5/3, 2017 at 20:38 Comment(3)
The await for(var x in stream) ... syntax is a closer equivalent to await stream.forEach((x) { ... }) than to listen, just like for (var x in iterable) ... is similar to iterable.forEach((x) { ... }). The differences come from the for loops allowing control flow that forEach doesn't.Sox
the link is brokenGalyak
Great explanation!Amphibolous
M
13

A more imporant difference is that await for serializes the consumption of the stream items while listen will process them concurrently.

For example the code below:

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;
}

yields

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
Miracidium answered 15/1, 2020 at 3:16 Comment(2)
This is a really important difference! It is what I was looking for to solve a certain problem, thanks!Aspect
Best answer ever!Hypnosis
P
9

The main difference is when there's code afterwards. listen only register the handler and the execution continue. await for will retain execution until the stream is closed.

Thus if you add a print('hello'); at the end of your main you shouldn't see hello in the output with await for (because the request stream is never closed). Try the following code on dartpad to see the differences :

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;
}
Pyrimidine answered 5/3, 2017 at 20:40 Comment(0)
O
1

Another difference can be the listen() returns you a StreamSubscription object, which can be used to cancel/pause the subscription at any later point of time. You can set callbacks to be called for each data event or error event, and when the stream is closed.

The below demonstrates that after listening to stream for 5 seconds, we will cancel it.

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
Orthoptic answered 24/1, 2021 at 3:10 Comment(0)
T
0

As Robson said:

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

I would also like to add a note that while use listen method possible to process stream events one by one if use pause and resume methods. Pause method should be called before first await keyword.

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");
}
Tap answered 20/9, 2021 at 18:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.