Async await not behaving properly - dart

Trying to figure out why this does not execute in the order I'm expecting it to. I'm expecting 2 to be the last to execute but it's completing before everything else finishes running.
Can't understand why it's returning the function after 4 and before listening to the stream.
Future mainFn() async {
print('Listen 1');
Ob o = await listenFn();
print('Listen 2');
return await someFn(o);
}
Future<Ob> listenFn() async {
o = new Ob();
print('Listen 3');
Stream<Ct> stream = await getCt();
print('Listen 4');
stream.listen((Ct _ct) {
print('Listen 7');
if (!cts.contains(_ct)) {
setState(() {
cts.add(_ct);
});
}
}, onError: (a) {
print('Listen 8');
print(a);
}, onDone: () async {
print('Listen 9');
o = await addOb(cts); <-- This function returns Future<Ob>
});
await stream.drain();
print('Listen 14');
return o;
}
Future<Stream<Ct>> getCt() async {
print('Listen 5');
final String url = 'API_URL';
final client = new http.Client();
final request = http.Request('get', Uri.parse(url));
final streamedRest = await client.send(request);
print('Listen 6');
return streamedRest.stream
.transform(utf8.decoder)
.transform(json.decoder)
.map((data) => Helper.getData(data))
.expand((data) => (data as List))
.map((data) {
return Ct.fromJSON(data);
});
}
I'm getting
I/flutter (22211): Listen 1
I/flutter (22211): Listen 3
I/flutter (22211): Listen 5
I/flutter (22211): Listen 6
I/flutter (22211): Listen 4
I/flutter (22211): Listen 2
I/flutter (22211): Listen 7 <-- 4 Ct items in the stream
I/flutter (22211): Listen 7
I/flutter (22211): Listen 7
I/flutter (22211): Listen 7
I/flutter (22211): Listen 9
I/flutter (22211): Listen 10 <-- added more of these along the way
I/flutter (22211): Listen 11
I/flutter (22211): Listen 12
I/flutter (22211): Listen 13

Here's a breakdown:
Enter mainFn, prints Listen 1.
mainFn calls and awaits listenFn.
Enter listenFn. Prints Listen 3.
listenFn calls and awaits getCt.
Enter getCt. Prints Listen 5.
getCt calls and awaits client.send.
client.send does its work and returns.
getCt resumes and prints Listen 6.
getCt returns a Stream.
listenFn resumes and prints Listen 4.
listenFn adds a listener to the Stream.
listenFn returns nothing.
mainFn resumes and prints Listen 2.
mainFn calls and await someFn.
The Stream emits events, triggering the listener callback.
The listener callback prints Listen 7 for each event.
The Stream eventually completes and prints Listen 9.
Where you go wrong is that you expect that listenFn will return the result of the onDone callback. listenFn does not wait for the Stream to run to completion; it registers a callback and returns immediately. If you run dartanalyzer on your code, you should get a warning mentioning that listenFn is declared to return a value but never returns anything.

So if anyone stumbles upon this in the future, I've chanced upon a solution to this. The answer is to use await for.
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.
await for(String s in stream) { print(s); }
Credit: https://stackoverflow.com/a/42613676/12689749

Related

Riverpod with hooks beaks when widget is disposedI

I have Flutter mobile app that is using Riverpod with hooks.
I have the following function that I would like to be called when the widget is disposed:
useEffect(
() {
final firestoreRepo =
ref.read(firebaseFirestoreRepositoryProvider);
return () async {
try {
// I get exception at this line.
// I need this future to be called when the
// widget is disposed.
// Calling this future earlier is not userful
// for my business logic.
final relationship =
await ref.read(relationshipWithProvider(pid).future);
if (relationship?.unseen ?? false) {
await firestoreRepo?.updateRelatinoship(pid: pid);
}
} catch (e, st) {
// print error
}
};
},
[],
);
I keep getting this error at the line shown in the comment above.
I/flutter ( 5967): Looking up a deactivated widget's ancestor is unsafe.
I/flutter ( 5967): At this point the state of the widget's element tree is no longer stable.
How can I sold this problem
We can initially get our relationship and then await and use it:
useEffect(
() {
final firestoreRepo = ref.read(firebaseFirestoreRepositoryProvider);
final relationship = ref.read(relationshipWithProvider(pid).future);
return () async {
try {
if (await relationship?.unseen ?? false) {
await firestoreRepo?.updateRelatinoship(pid: pid);
}
} catch (e, st) {
// print error
}
};
},
[],
);
As far as I can tell, this won't contradict the logic of the business process, because one way or another, we'll have to make the relationshipWithProvider(pid) request early (when we initialize the widget) or late (when we delete the widget).

how can i do something if user didnt input anything in console in dart?

I am new in dart and creating a console game.
we have:
int score = 0;
i should give every time a number from user in console environment.
if user input a number than score plus one...
and if user did not input anything and he did not touch his keyboard after 5 seconds the score should be minus one.
please help!
I tried this page sulotion:
How to get keyboard user input within a timeframe
but when use that code in a loop to run the other round of game
as you see below:
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
for(var i = 0; i < 10; i++) {
print('You have 5 seconds! WHAT IS YOUR NAME!!!!');
final name = await stdin
.transform(const Utf8Decoder())
.transform(const LineSplitter())
.timeout(const Duration(seconds: 5), onTimeout: (sink) =>
sink.add('2'))
.first;
if (name == null) {
print('LOL WUT DO YOUR NOT EVEN KNOW YOUR NAME!!!!');
} else {
print('I knew it. Your name is $name!');
}
}
}
that give me this error:
Unhandled exception:
Bad state: Stream has already been listened to.
#0 _StreamController._subscribe (dart:async/stream_controller.dart:676:7)
#1 _ControllerStream._createSubscription (dart:async/stream_controller.dart:827:19)
#2 _StreamImpl.listen (dart:async/stream_impl.dart:471:9)
#3 _Socket.listen (dart:io-patch/socket_patch.dart:2184:31)
#4 _StdStream.listen (dart:io/stdio.dart:22:20)
#5 new _SinkTransformerStreamSubscription (dart:async/stream_transformers.dart:49:16)
#6 _BoundSinkStream.listen (dart:async/stream_transformers.dart:171:9)
#7 new _SinkTransformerStreamSubscription (dart:async/stream_transformers.dart:49:16)

Assert all the items emitted by a Stream in order until it is canceled in Dart?

Is there any convenient way to assert all the items emitted by a Stream in order until it is canceled?
If I use:
expectLater(
stream,
emitsInOrder(<String>[
'item1',
'item2',
]),
);
and the Stream emits ['item1', 'item2', 'item3'] , the test won't fail.
The only way I've found so far is the following:
var count = 0;
final expected = ['item1', 'item2', 'item3'];
stream.listen(
expectAsync1(
(final result) {
expect(result, expected[count++]);
},
count: expected.length,
),
);
But it is a bit verbose and not very easy to read. Is there a simpler/more elegant way?
You can collect the items into a list, using toList, then compare it to your own expectation list:
await expectLater(stream.toList(), completion(expected));
This does not handle the case where the stream doesn't close at all (but then, nothing does, you just have to wait for a timeout).
It doesn't catch errors until all events have been emitted, the emitsInOrder approach is better for that. Not shorter, though.
emitsDone can be used if the Stream is closed at some point.
E.g:
test('Test', () async {
final controller = StreamController<String>();
final stream = controller.stream;
final matcher = expectLater(
stream,
emitsInOrder(<dynamic>[
'Item1',
'Item2',
emitsDone,
]),
);
controller
..add('Item1')
..add('Item2')
..add('Item3')
..close();
await matcher;
await controller.close();
});
The test fails with error:
Expected: should do the following in order:
• emit an event that 'Item1'
• emit an event that 'Item2'
• be done
Actual: <Instance of '_ControllerStream<String>'>
Which: emitted • Item1
• Item2
• Item3
x Stream closed.
which didn't be done
As #Irn suggest, a more compact alternative for Streams that complete at some point is using toList:
test('Test', () async {
final controller = StreamController<String>();
final stream = controller.stream;
final matcher = expectLater(stream.toList(), completion(<String>['Item1', 'Item2']));
controller
..add('Item1')
..add('Item2')
..add('Item3')
..close();
await matcher;
await controller.close();
});
If the Stream is never closed, you can add a timeout and check the items that have been emitted in that period:
test('Test3', () async {
final controller = StreamController<String>();
final stream = controller.stream.timeout(const Duration(milliseconds: 200));
final matcher = expectLater(
stream,
emitsInOrder(<dynamic>[
'Item1',
'Item2',
]),
);
controller
..add('Item1')
..add('Item2');
await matcher;
await controller.close();
});

Load firebase database list with listener asynchronous to future

I want to load with flutter a list from realtime firebase database and wait for finished loading. I expect that
FirebaseDatabase database = new FirebaseDatabase();
Query _newsQuery = database
.reference()
.child('news')
.orderByChild('published')
.limitToFirst(10);
Future<List<News>> loadNews() async {
List<News> list = new List<News>();
_newsQuery.onChildAdded.listen(onNewsAdded);
return list;
}
Future<News> onNewsAdded(Event event) async {
News n = News.fromSnapshot(event.snapshot);
print(n.title);
return n;
}
gives the result 2 with two database-entries when i excecute it with
loadNews().then((List<News> newsList) {
print(newsList.length);
});
But i get
I/flutter (19206): 0
I/flutter (19206): Title 1
I/flutter (19206): Title 2
That means that the listener is not async waiting. I tried a lot of different lines, but not found a solution to wait for the last query result.
I NOT want to use setState() in any widget, because i want to use a own singleton databaseHandler for all the widgets. For that reason is want to load the data async and wait for it in the widget.
Ok, i think i found a solution. After a lot of trial and errors i get an idea with https://webdev.dartlang.org/articles/performance/event-loop. Used and forced with that a nested future to fill the list. Used then Completer to close the list.
Future<List<News>> loadNews() async {
Completer c = new Completer<List<News>>();
List<News> list = new List<News>();
Stream<Event> sse = _newsQuery.onChildAdded;
sse.listen((Event event) {
onNewsAdded(event, list).then((List<News> newsList) {
return new Future.delayed(new Duration(seconds: 0), ()=> newsList);
}).then((_) {
if (!c.isCompleted) {
c.complete(list);
}
});
});
return c.future;
}
Future<List<News>> onNewsAdded(Event event, List<News> newsList) async {
News n = News.fromSnapshot(event.snapshot);
print("ADD: "+n.title);
newsList.add(n);
return newsList;
}
With that i get the result
I/flutter ( 5419): ADD: Title 1
I/flutter ( 5419): ADD: Title 2
I/flutter ( 5419): ADD: Title 3
I/flutter ( 5419): Size: 3

Difference between await for and listen in Dart

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?
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.
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;
}
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
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
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");
}

Resources