How to implement a fire and forget function in Dart - dart

I have the following Dart program:
import 'package:pedantic/pedantic.dart';
Future someFunc() async {
for (var i = 0; i < 100; i++) {
print('Value of i in someFunc(): ' + i.toString());
}
}
Future someOtherFunc() async {
for (var i = 0; i < 100; i++) {
print('Value of i in someOtherFunc(): ' + i.toString());
}
}
Future<int> main() async {
unawaited(someFunc());
unawaited(someOtherFunc());
print('End of program...');
return 0;
}
When I execute this, the output is:
Value of i in someFunc(): 0
Value of i in someFunc(): 1
Value of i in someFunc(): 2
...
Value of i in someFunc(): 99
Value of i in someOtherFunc(): 0
Value of i in someOtherFunc(): 1
Value of i in someOtherFunc(): 2
...
Value of i in someOtherFunc(): 99
End of program...
It looks like everything was executed synchronously. I was expecting something like:
Value of i in someFunc(): 0
Value of i in someFunc(): 1
Value of i in someOtherFunc(): 0
Value of i in someFunc(): 3
End of program...
Value of i in someOtherFunc(): 1
(etc...)
How do I create an asynchronous "fire and forget" function/method in Dart?

The reason you're seeing this code execute synchronously is because asynchronous methods in Dart execute as if they're synchronous until the first await, at which point execution of that method is suspended until the await completes. If you were performing actual asynchronous work in your methods, you'd see interleaving execution like you expect.
For example, here's some slight modifications to your code that will roughly result in the behavior you expect:
import 'package:pedantic/pedantic.dart';
Future someFunc() async {
for (var i = 0; i < 100; i++) {
// Future.microtask is used to schedule work on the microtask queue,
// which forces this method to suspend execution and continue on to someOtherFunc.
// If you were doing some actual asynchronous work, you wouldn't need this.
await Future.microtask(() => print('Value of i in someFunc(): ' + i.toString()));
}
}
Future someOtherFunc() async {
for (var i = 0; i < 100; i++) {
await Future.microtask(() => print('Value of i in someOtherFunc(): ' + i.toString()));
}
}
Future<int> main() async {
unawaited(someFunc());
unawaited(someOtherFunc());
print('End of program...');
return 0;
}
I highly recommend reading up on the Dart event loop and microtask queue, which will give you a good understanding of how asynchrony works in Dart

Related

Understanding Future.delayed()

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

Dart avait Future, postpone just certain line of code and execute the rest

Want to have certain lines of code postponed, using await Future for it and it works great, problem is that it postpones all the code after it, I need it to postpone just certain line of code while continuing to execute rest of the code imediately
void main() async {
for (int i = 0; i < 5; i++) {
await Future.delayed(Duration(seconds: 1));
//postpone just next line or few lines of code
print('postpone this line of code ${i + 1}');
print('postpone me too');
}
//should execute without being postponed
print('continue imediately without being postponed by await Future');
}
Is this possible with await Future or with some other function?
await it syntactic sugar for registering a Future.then callback. The point of using await is to make it easier to make all of the subsequent code wait for the Future to complete. If that's not what you want, you can use Future.then directly:
void main() {
for (int i = 0; i < 5; i++) {
Future.delayed(const Duration(seconds: 1)).then((_) {
print('postpone this line of code ${i + 1}');
print('postpone me too');
});
}
print('continue immediately without being postponed by await Future');
}
Since Future.delayed takes a callback, you can also skip the then completely:
void main() {
for (int i = 0; i < 5; i++) {
Future.delayed(const Duration(seconds: 1), () {
print('postpone this line of code ${i + 1}');
print('postpone me too');
});
}
print('continue immediately without being postponed by await Future');
}
If you don't use the created future for anything, this is equivalent to using a Timer:
import 'dart:async' show Timer;
void main() {
for (int i = 0; i < 5; i++) {
Timer(const Duration(seconds: 1), () {
print('postpone this line of code ${i + 1}');
print('postpone me too');
});
}
print('continue immediately without being postponed by await Future');
}

Dart program exits without executing last statement

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

Dart File.writeAsString() method does not write to file if await is not done immediately

I have the following Dart code that doesn't behave as I expected:
final File file = File("result.csv");
Future send(String message) async {
try {
await file.writeAsString(message + '\n',
mode: FileMode.append, flush: true);
} catch (e) {
print("Error: $e");
}
return await file.length();
}
main() async {
final futures = <Future>[];
for (int i = 0; i < 100; i++) {
futures.add(send("$i"));
}
for (int i = 0; i < 100; i++) {
print(await futures[i]);
}
}
I expected the file to be written as soon as each call to await futures[i] in the second loop returned. However this does not seem to be happening.
The file should contain one line for each index from 0 to 99. But it contains a line with 99 followed by an empty line. And the print calls in the second loop always print the same file length, 3.
The event loop seems to be somehow merging the calls and only actually executing the last call, even though I still get 100 different futures that I await in the second loop.
Why is this happening and how can I allow the futures to run without awaiting them immediately (I really need to await only later, when all of the calls to send have been made)?
With the loop:
for (int i = 0; i < 100; i++) {
futures.add(send("$i"));
}
multiple send are immediately invoked and each one concurrently open and write a string in the file: you have a race condition and at the ends it happens that only one message it is written to the file.
Use a list of functions that return a future
With a closure it is possible to implement a sequenzialized version for file access that avoid the race condition.
Instead of creating a list of futures, create a list of functions that returns a future:
The shared file resource is accessed sequentially if you call and await such functions in a loop:
import 'dart:io';
final File file = File("result.csv");
typedef Future SendFunction();
Future send(String message) async {
try {
await file.writeAsString(message + '\n',
mode: FileMode.append, flush: true);
} catch (e) {
print("Error: $e");
}
var l = await file.length();
return l;
}
main() async {
final futures = List<SendFunction>();
//final futures = <Future>[];
for (int i = 0; i < 100; i++) {
//futures.add(send("$i"));
futures.add(() => send("$i"));
}
for (int i = 0; i < 100; i++) {
print(await futures[i]());
//print(await futures[i]);
}
}
synchronized package
synchronized offers lock mechanism to prevent concurrent access to asynchronous code.
In this case it may be used to avoid concurrent access to result.csv file:
import 'dart:io';
import 'package:synchronized/synchronized.dart';
final File file = File("result.csv");
final lock = new Lock();
Future send(String message) async {
try {
await file.writeAsString(message + '\n',
mode: FileMode.append, flush: true);
} catch (e) {
print("Error: $e");
}
return await file.length();
}
main() async {
final futures = <Future>[];
for (int i = 0; i < 100; i++) {
futures.add(lock.synchronized(() => send("$i")));
}
for (int i = 0; i < 100; i++) {
print(await futures[i]);
}
}

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