I have a code in Dart like this:
Future<void> dataProcessAsync() async {
await Future.delayed(Duration(seconds: 2));
print("Process Completed!");
}
void main() {
print("Process 1");
print("Process 2");
dataProcessAsync();
print("Process 3");
print("Process 4");
}
Everything works fine and asynchronously. The result is as expected
(Process 1 - Process 2 - Process 3 - Process 4 - Process Completed!)
But when i write the code like this:
Future<void> dataProcessAsync() async {
for(int i = 1; i <= 10000000000; i++){}
print("Process Completed!");
}
void main() {
print("Process 1");
print("Process 2");
dataProcessAsync();
print("Process 3");
print("Process 4");
}
It doesn't work asynchronously. It waited for the dataProcessAsync() for a quite long time then
continue with Process 3. (Process 1 - Process 2 - Process Completed! - Process 3 - Process 4)
Can someone tell me whats going on and how to solve this ?
async methods are running synchronously until the first await. If the method are never reaching an await, it will run to completion and return a Future which is filled synchronously.
This is by design and described on the Dart webpage:
An async function runs synchronously until the first await keyword. This means that within an async function body, all synchronous code before the first await keyword executes immediately.
https://dart.dev/codelabs/async-await#execution-flow-with-async-and-await
Related
I am failing to understand, why the error thrown from addItem method in below code is not caught in the try-catch block
void main() async {
var executor = Executor();
var stream = Stream.fromIterable([0, 1, 2, 3, 4, 5, 6, 7]);
try {
await for (var _ in stream) {
executor.submit(() => demoMethod());
}
await executor.execute();
} catch (e) {
print(e);
}
}
Future<void> demoMethod() async {
var list = [1, 2, 3, 1, 4, 5];
var executor = Executor();
var test = Test();
for (var element in list) {
executor.submit(() => test.addItem(element));
}
await executor.execute();
test.list.forEach(print);
}
class Test {
var list = <int>[];
Future<void> addItem(int i) async {
if (list.contains(i)) {
throw Exception('Item exists');
}
list.add(i);
}
}
class Executor {
final List<Future<void>> _futures = [];
bool _disposed = false;
void submit(Future<void> Function() computation) {
if (!_disposed) {
_futures.add(computation());
} else {
throw Exception('Executor is already disposed');
}
}
Future<void> execute() async {
await Future.wait(_futures, eagerError: true);
_disposed = true;
}
}
but below code is able to catch the error properly
void main() async {
var executor = Executor();
try {
for (var i = 0; i < 10; i++) {
executor.submit(() => demoMethod());
}
await executor.execute();
} catch (e) {
print(e);
}
}
I am guessing it has something to do with the stream processing.
It's the stream.
In your other examples, you synchronously run through a loop a and call Executor.submit with all the computations, then immediately call executor.execute().
There is no asychronous gap between calling the function which returns a future, and Future.wait starting to wait for that future.
In the stream code, each stream events starts an asynchronous computation by calling Executor.submit. That creates a future, stores it in a list, and goes back to waiting for the stream.
If that future completes, with an error, before the stream ends and Future.wait gets called, then there is no error handler attached to the future yet. The error is then considered unhandled, and is reported to the current Zone's uncaught error handler. Here that's the root zone, which means it's a global uncaught error, which may crash your entire program.
You need to make sure the future doesn't consider its error unhandled.
The easiest way to do that is to change submit to:
void submit(Future<void> Function() computation) {
if (!_disposed) {
_futures.add(computation()..ignore());
} else {
throw StateError('Executor is already disposed');
}
}
The ..ignore() tells the future that it's OK to not have an error handler.
You know, because the code will later come back and call executor.execute, that any errors will still be reported, so it should be safe to just postpone them a little. That's what Future.ignore is for.
(Also changed Exception to StateError, because that's what you should use to report people using objects that have been disposed or otherwise decommissioned.)
When I try to run the below code, it completes in a little more than 4 seconds. I couldn't understand why it finishes in that time. I thought it would complete in 14 seconds(4sec in declaring order variable,10sec in for loop). Don't Future.delayed() stop all the progress in program?
Future<void> printOrderMessage() async {
print("Awaiting user order ...");
var order = await fetchUserOrder(); //I couldn't understand here.
print('Your order is: $order');
}
Future<String> fetchUserOrder() {
return Future.delayed(const Duration(seconds: 4), () => 'Large Latte');
}
void main() async {
countSeconds(4); //Başlama yeri
await printOrderMessage();
}
void countSeconds(int s) {
for (var i = 1; i <= s; i++) {
Future.delayed(Duration(seconds: i), () => print(i)); //Also here
}
}
Output:
Awaiting user order ...
1
2
3
4
Your order is: Large Latte
I have a Dart application which receives udp datagram every 10ms. I want to know when data stream is stopped. I have a RawDatagramSocket as _udpSocket I can listen datagram by using this function:
RawDatagramSocket? _udpSocket;
Future<void> bindSocket() async {
_udpSocket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, port);
setTimeout();
}
Future<void> listen() async {
_udpSocket?.listen((event) async {
Datagram? d = _udpSocket?.receive();
if (d == null) {
return;
}
//handle received data
});
}
And also I have a function to handle timeout:
void setTimeout() {
//if there is no data receiving in a couple of cycle, time out will be triggered.
//1 cycle == 10ms, I want timeout to be triggered after 10 cycles. (100ms)
_udpSocket?.timeout(timeoutDuration, onTimeout: (sink) {
//Handle time out
});
}
I am able to receive and process incoming data, but timeout function is not working.
Is there anything wrong with my code, or is there any other method to do what I want.
I figured it out,I updated the listen function. Here is the update for those who would need it:
final Duration timeoutDuration = const Duration(milliseconds: 100);
#override
Future<void> listen() async {
_udpSocket?.timeout(timeoutDuration, onTimeout: ((sink) {
//do your work when data stream closed
})).listen((event) async {
Datagram? d = _udpSocket?.receive();
if (d == null) {
return;
}
//handle received data
});
}
I hope it will be useful.
I'm trying to understand Streams and wrote some code.
Everything seems to work, the program exits with status code 0. But it doesn't print the 'loop done' and 'main done' strings. I can't figure out why.
import 'dart:async';
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
class Retry {
StreamController<int> _outgoing;
Retry(Stream<int> incoming) {
_outgoing = StreamController<int>();
_outgoing.addStream(incoming);
}
Future<void> process() async {
await for (final i in _outgoing.stream) {
print("got $i");
}
print('loop done'); // Not printed
}
}
void main() async {
var stream = countStream(4);
var retry = Retry(stream);
await retry.process();
print('main done'); // Not printed
}
The _outgoing.stream is never closed, so code after the await for will never execute. The VM does notice that there also won't be any new events on that stream so nothing else will ever happen, and it can exit. You could fix the bug with:
_outgoing.addStream(incoming).whenComplete(() {
_outgoing.close();
});
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");
}