Dart waiting for an async function to complete without await - asynchronous

I've narrowed my problem down to the below code. I'm new to Dart, and I can't seem to figure out why the code won't move on to printing "done" before print the numbers 1-10.
import 'dart:async';
Future<bool> wait() async {
for(int i = 0; i < 10; i++){
print('$i');
}
return true;
}
Future testAsync() async {
print('starting');
wait();
print('done');
}
main(List<String> arguments){
testAsync();
}
Since there is no await keyword in front of wait(); shouldn't the program execute print('done'); and then print out the numbers? For some reason, it is waiting for wait(); to finish anyway.

This is expected behavior since Dart 2.0.0:
(Breaking) Functions marked async now run synchronously until the first await statement. Previously, they would return to the event loop once at the top of the function body before any code runs (issue 30345).
https://github.com/dart-lang/sdk/blob/stable/CHANGELOG.md
Since you async method does not actually wait on anything, the entire method is therefore executed synchronously.
You can change you code into something like this to get the behavior your want:
import 'dart:async';
Future<bool> wait() async {
await Future.microtask(() {
for (int i = 0; i < 10; i++) {
print('$i');
}
});
return true;
}
Future testAsync() async {
print('starting');
wait();
print('done');
}
main(List<String> arguments) {
testAsync();
}

Related

firestore, coroutine and flow

firebase method is working on worker thread automatically. but I have used coroutine and callbackflow to implement firebase listener code synchronously or get return from the listener.
below is my code that I explained
coroutine await with firebase for one shot
override suspend fun checkNickName(nickName: String): Results<Int> {
lateinit var result : Results<Int>
fireStore.collection("database")
.document("user")
.get()
.addOnCompleteListener { document ->
if (document.isSuccessful) {
val list = document.result.data?.get("nickNameList") as List<String>
if (list.contains(nickName))
result = Results.Exist(1)
else
result = Results.No(0)
//document.getResult().get("nickNameList")
}
else {
}
}.await()
return result
}
callbackflow with firebase listener
override fun getOwnUser(): Flow<UserEntity> = callbackFlow{
val document = fireStore.collection("database/user/userList/")
.document("test!!!!!")
val subscription = document.addSnapshotListener { snapshot,_ ->
if (snapshot!!.exists()) {
val ownUser = snapshot.toObject<UserEntity>()
if (ownUser != null) {
trySend(ownUser)
}
}
}
awaitClose { subscription.remove() }
}
so I really wonder these way is good or bad practice and its reason
Do not combine addOnCompleteListener with coroutines await(). There is no guarantee that the listener gets called before or after await(), so it is possible the code in the listener won't be called until after the whole suspend function returns. Also, one of the major reasons to use coroutines in the first place is to avoid using callbacks. So your first function should look like:
override suspend fun checkNickName(nickName: String): Results<Int> {
try {
val userList = fireStore.collection("database")
.document("user")
.get()
.await()
.get("nickNameList") as List<String>
return if (userList.contains(nickName)) Results.Exist(1) else Results.No(0)
} catch (e: Exception) {
// return a failure result here
}
}
Your use of callbackFlow looks fine, except you should add a buffer() call to the flow you're returning so you can specify how to handle backpressure. However, it's possible you will want to handle that downstream instead.
override fun getOwnUser(): Flow<UserEntity> = callbackFlow {
//...
}.buffer(/* Customize backpressure behavior here */)

Vala yield not returning

I'm having trouble writing an async function in Vala.
I understand that the function's callback has to get registered somewhere in order for execution to continue after the yield statements, so I add it to the GLib mainloop to be called periodically using Idle.add. Here is my dummy code:
async void loop_func()
{
var id = Idle.add(loop_func.callback);
message("Added idle %u",id);
yield;
message("yielded successfully 1");
yield;
message("yielded successfully 2");
for (var i = 0; i < 20; i++)
{
message("%d",i);
yield;
}
message("finished");
}
int main()
{
var loop = new GLib.MainLoop();
loop_func.begin(() => {loop.quit();});
loop.run();
return 0;
}
Despite this, the code after the 2nd yield statement never gets executed.
This can be seen from the output:
$ ./async
** Message: 20:07:24.932: async.vala:4: Added idle 1
** Message: 20:07:24.932: async.vala:7: yielded successfully 1
And then it hangs.
What am I missing here?
Although GLib calls an idle function repeatedly until it tells it to stop (by returning false), the async function's .callback seems to quit repeating immediately after being called.
This can be overcome by adding the callback again every time before the yield statement:
async void loop_func()
{
for (var i = 0; i < 20; i++)
{
Idle.add(loop_func.callback);
yield;
}
}
An alternative (and possibly more effective) way to do this would be to ignore the value that the async function's callback returns:
async void loop_func()
{
var id = Idle.add(() => {loop_func.callback(); return Source.CONTINUE;});
for (var i = 0; i < 20; i++)
{
yield;
}
Source.remove(id);
}

asynchronous programming in dart (Futures, await)

what changes should I do to the following code to get the following output:
Task 1 complete
Task 4 complete
Task 2 complete
Task 3 complete with task 2 data
I currently getting the outputs given below:
Task 1 complete
Task 2 complete
Task 3 complete with task 2 data
Task 4 complete
import 'dart:async';
void main() {
performTasks();
}
void performTasks() async {
task1();
String task2Result = await task2();
task3(task2Result);
task4();
}
void task1() {
String result = 'task 1 data';
print('Task 1 complete');
}
Future<String> task2() async {
Duration threeSeconds = Duration(seconds: 3);
String result;
await Future.delayed(threeSeconds, () {
result = 'task 2 data';
print('Task 2 complete');
});
return result;
}
void task3(String task2Data) {
String result = 'task 3 data';
print('Task 3 complete with $task2Data');
}
void task4() {
String result = 'task 4 data';
print('Task 4 complete');
}
Don't call task4 after waiting for task2.
So:
void performTasks() async {
task1();
task4();
String task2Result = await task2();
task3(task2Result);
}
That looks pretty obvious, so I'm assuming your real problem is more complicated, and you can't move test4() around like that.
In that case, you should not use await. The await ensures that everything written after the await also executes after the awaited future has completed.
Instead you can fall back on the Future API:
void performTasks() { // no `async`
task1();
task2().then(task3); // no `await`
task4();
}
This sets up task2() to run, and when that is done, it calls task3 with the result. It doesn't wait for anything, though, and it executes task4 immediately after setting this up.
The then method on futures takes a callback, and eventually calls that with the result of the future. Here task3 takes one argument, so it can directly be used as that callback.
That assumes that task2's result is directly usable as an argument to task3. If not, and you have to capture the result and manipulate it first, you'd do it as:
void performTasks() { // no `async`
task1();
task2().then((result2) { // still no await here!
var argument3 = manipulate(result2);
// ... and whatever else you want to do
// between task2 completing and task3 starting.
task3(argument3);
});
task4();
}
in performTasks(), you should move task4(); right after task1();.

How do I operate List async by using Dart?

I have a collection, and now I need to iterate through the data in it, and then modify one of the fields, but this modification process is time consuming and requires asynchronous, I do not know how to write this code correctly.
Here are my fake code:
Stream<int> asynchronousNaturalsTo(int n) async* {
yield calculate(n).asStream();
}
Future<int> calculate(int i) async {
// async calculate result. maybe there is some long-running operate.
return Future.value(i + 10);
}
main() async {
// 1. I have a List that contains data needs be calculated by async.
for (int i = 0; i < 3; i++) {
// 2. calculate
var list = await asynchronousNaturalsTo(i);
// 3. finally, use the final result.
print(list);
}
}
How do I write the code?
thx...

Wrong order of execution in Flutter asynchronous code

I am having problems with futures in a Flutter app.
void saveCats() async {
var cats = await convertToCats(_rawData);
await DatabaseProvider.db.addCats(cats);
}
Future<List<Cat>> convertToCats(CatList catData) async {
var cats = <Cat>[];
await catData.forEach(key, value) async {
var pos = await calculatePos();
print('This should come first');
cats.add(Cat(name: key, pos: pos);
}
}
Future<int> calculatePos() async {
return await DatabaseProvider.db.calculatePos();
}
database.dart:
Future<void> addCats(List<Cat> cats) async {
print('This should come second');
// Do adding stuff here
}
Future<int> calculatePos() async {
// Some code to calculate the position
return pos;
}
In the above code, the saveCats function is called when a button is tapped. This function converts some raw data to a list of Cat models, and adds them to the database. As part of this conversion process, it calculates the pos of the cat in a list. My problem is that I would expect that, of my two print statements, the one in the forEach loop should come before the one in the addCats database function. But instead they appear in reverse order. Where am I going wrong?
You can't async/await in List.forEach() or Map.forEach() as both of them return void.
Either use
await Future.forEach([1, 2, 3], (num) async {
await asyncMethod(num);
});
or something similar;
https://api.flutter.dev/flutter/dart-async/Future/forEach.html
forEach often doesn't do what you expect, because the provided function runs as a closure.
It's more natural when you want to iterate over a list doing something to each element to use for (or one of the more functional type methods like map).
It's not clear what type CatList is, so this is approximate, but you'll want something more like:
Future<List<Cat>> convertToCats(CatList catData) async {
var cats = <Cat>[];
for (var i = 0; i < catData.length; i++) {
var key = catData[i].key;
var pos = await calculatePos();
print('This should come first');
cats.add(Cat(name: key, pos: pos));
}
return cats;
}
or
Future<List<Cat>> convertToCats(CatList catData) async {
return catData.map(...some mapping function...).toList();
}

Resources