awaiting on an async function does not execute correctly in dart - asynchronous

I have a few async functions in my dart program which interact with SQLite database. I use await expression to invoke those functions and mostly the functions are executed when they're awaited on but one function does not execute and the calling function continues execution without awaiting on the called function. Here's the code:
Future<int> addShoppingList(String listName) async {
var dbClient = await db;
String now = new DateTime.now().toString();
await dbClient.transaction((txn) async {
int res = await txn.rawInsert("insert into lists(list_name,list_created_at) values(\'$listName\',\'$now\')");
print('result of adding a new shopping list: $res');
return res;
});
List<Map> resList = await dbClient.rawQuery("select list_id from lists where list_name=\'$listName\'");
if (resList.length > 0) {
return resList[0]['list_id'];
}
return 0;
//await dbClient.rawInsert("insert into lists(list_name,list_created_at) values(\'$listName\',\'$now\')");
}
Future<int> addShoppingListItems(int listId, Map<String,String> listItems) async {
var dbClient = await db;
int res = 0;
listItems.forEach((itemName, quantity) async{
int itemId = await getItemId(itemName);
print('adding item $itemName with id $itemId');
await dbClient.transaction((txn) async {
res = await txn.rawInsert("insert into list_items values($listId,$itemId,\'$quantity\')");
print('result of adding item in list_items: $res');
});
return res;
});
return 0;
}
Future<int> addItemsToShoppingList(String listName, Map<String,String> listItems) async {
int listId = await getListId(listName);
if (listId == 0) {
listId = await addShoppingList(listName);
print('got list id of $listId after adding new list');
}
print('in additemstoshoppinglist list id: $listId');
print('in additemstoshoppinglist ${listItems.toString()}');
int res = await addShoppingListItems(listId, listItems);
print('result after adding item in addItemsToShoppingList: $res');
return res;
}
In my external class I await on addItemsToShoppingList function to start the whole chain of functions. When I run the code, I see that the functions getListId(), addShoppingList() are awaited correctly(the execution of addItemsToShoppingList does not proceed until the awaited functions are executed) but when I await on addShoppingListItems, the calling function's execution does not wait until addShoppingListItems returns. What am I missing here and how can I make sure addShoppingListItems returns before the execution of calling function can proceed?

I figured out that the problem was with the way async functions are awaited upon in the for each loop of the map being used. I solved this problem with the solution given here: Map forEach function exits with an await inside

Related

Flutter Firebase async query not retrieving data inside a stream function

I am trying to query a User from firebase within another query but for some reason but I can't get the code to work
The function the wont run is await usersRef.doc(uid).get(); and can be found here:
static getUserData(String uid) async {
return await usersRef.doc(uid).get();
}
static DirectMessageListModel getDocData(QueryDocumentSnapshot qdoc, String uid) {
Userdata postUser = Userdata.fromDoc(getUserData(uid));
return DirectMessageListModel.fromDoc(qdoc, postUser);
}
static DirectMessageListModel fromDoc(QueryDocumentSnapshot doc, Userdata altUser) {
return DirectMessageListModel(
doc['chatId'],
doc['lastMsgContent'],
doc['lastMsgType'],
altUser
);
}
parent function:
Stream<List<DirectMessageListModel>> getMeassageList(){
var snaps = FirebaseFirestore.instance.collection('directMessages').where('users', arrayContains: userdata!.uid).snapshots();
List<String> usersListElement = [];
return snaps.map((event) { return event.docs.map((e) {
usersListElement = [e.get('users')[0], e.get('users')[1]];
usersListElement.remove(userdata!.uid);
return DirectMessageListModel.getDocData(e, usersListElement.first);
}).toList();
});
}
You forgot to wait for the future getUserData(uid) to complete.
Try this:
static Future<DocumentSnapshot<Object>> getUserData(String uid) async {
return await usersRef.doc(uid).get();
}
static DirectMessageListModel getDocData(
QueryDocumentSnapshot qdoc,
String uid,
) async {
Userdata postUser = Userdata.fromDoc(await getUserData(uid)); // await here
return DirectMessageListModel.fromDoc(qdoc, postUser);
}
..
// parent function.
// Also wait for the future in the parent function.
// UPDATE BELOW! Define the parent function like this:
Stream<List<Future<DirectMessageListModel>>> getMeassageList() {
var snaps = FirebaseFirestore.instance
.collection('directMessages')
.where('users', arrayContains: userdata!.uid)
.snapshots();
List<String> usersListElement = [];
return snaps.map((event) {
return event.docs.map((e) async {
usersListElement = [e.get('users')[0], e.get('users')[1]];
usersListElement.remove(userdata!.uid);
return await DirectMessageListModel.getDocData(e, usersListElement.first);
}).toList();
});
}
NB: You are fetching user data (either sender/receiver) for each message in directMessages collection. It might be better to store just sender/receiver name in directMessages collection and simply display that. Then if the user clicks on a message, you can then fetch the full sender/receiver data.

who to used Dismissible to delete the task from a database, using flutter and sqlite?

All!
I want to delete the task by using a Dismissible widget.
I try the following
onDismissed: (direction) {
DatabaseHelper.instance.deleteTask(task.id);
_updateTaskList();
},
and it works but the animation looks wrong as if the dismissed child reappear for a second and then disappears.
I think what is happening is that the deleteTask has a delay and the _updateTaskList it's running almost at the same time.
this are my methods:
Future<int> deleteTask(int id) async {
Database db = await this.db;
final int result = await db.delete(
tasksTable,
where: '$colId = ?',
whereArgs: [id],
);
return result;
}
}
_updateTaskList() {
setState(() {
_taskList = DatabaseHelper.instance.getTaskList();
});
}
Future<List<Task>> getTaskList() async {
final List<Map<String, dynamic>> taskMapList = await getTaskMapList();
final List<Task> taskList = [];
taskMapList.forEach((taskMap) {
taskList.add(Task.fromMap(taskMap));
});
return taskList;
}

await an anonymous function

In the following code I can't find a solution to await for the inner asynchronous code. The result is that the data is always returned empty. Any ideas?
Future<Map> resolveLinks(links) async {
var data = {};
(links as Map).forEach((k, v) async {
switch (v["type"]) {
case "address":
data[k] = await getAddress(v["value"]);
break;
case "person":
data[k] = await getPerson(v["value"], v["subtype"]);
break;
}
});
return data;
}
The reason why this won't work is because you inner function are returning Future objects you are never awaiting.
It should work if you modify your code to the following:
Future<Map> resolveLinks(links) async {
var data = {};
await Future.wait((links as Map).entries.map((entry) async {
final k = entry.key;
final v = entry.value;
switch (v["type"]) {
case "address":
data[k] = await getAddress(v["value"]);
break;
case "person":
data[k] = await getPerson(v["value"], v["subtype"]);
break;
}
}));
return data;
}
So instead of using forEach, I am using the map method on the entries iterable to map each entry into a Future object. When I takes all this Future objects and puts them into Future.wait which returns a Future there completes when all the other Future objects has returned its value.

Flutter Return Length of Documents from Firebase

Im trying to return the length of a list of documents with this function:
Future totalLikes(postID) async {
var respectsQuery = Firestore.instance
.collection('respects')
.where('postID', isEqualTo: postID);
respectsQuery.getDocuments().then((data) {
var totalEquals = data.documents.length;
return totalEquals;
});
}
I'm initialize this in the void init state (with another function call:
void initState() {
totalLikes(postID).then((result) {
setState(() {
_totalRespects = result;
});
});
}
However, when this runs, it initially returns a null value since it doesn't have time to to fully complete. I have tried to out an "await" before the Firestore call within the Future function but get the compile error of "Await only futures."
Can anyone help me understand how I can wait for this function to fully return a non-null value before setting the state of "_totalRespsects"?
Thanks!
I think you're looking for this:
Future totalLikes(postID) async {
var respectsQuery = Firestore.instance
.collection('respects')
.where('postID', isEqualTo: postID);
var querySnapshot = await respectsQuery.getDocuments();
var totalEquals = querySnapshot.documents.length;
return totalEquals;
}
Note that this loads all documents, just to determine the number of documents, which is incredibly wasteful (especially as you get more documents). Consider keeping a document where you maintain the count as a field, so that you only have to read a single document to get the count. See aggregation queries and distributed counters in the Firestore documentation.
Perfect code for your problem:
int? total;
getLength() async {
var getDocuments = await DatabaseHelper.registerUserCollection
.where("register", isEqualTo: "yes")
.get();
setState(() {
total = getDocuments.docs.length;
});
}
#override
void initState() {
super.initState();
getLength();
if (kDebugMode) {
print(total);
}
}

In a Dart async method, can certain lines that don't share any dependencies be run asynchronously?

Suppose you have an async method body, like below, with a method call that returns a Future and a print following it that doesn't not need the result of that method. Is it possible to add some structure to get the print statement to execute independently? Or does this syntactic sugar force an async method body to wholly run sequentially?
Future<String> test2() {
Completer c = new Completer();
new Timer(new Duration(seconds: 2), () {
c.complete('test2: done');
});
return c.future;
}
Future<String> test1() async {
String result = await test2();
print(result);
// Why is this not executed immediately before test2 call completes?
print('hello');
return 'test1: done';
}
void main() {
test1().then((result) => print(result));
}
Follow up: I've added below a rewrite of test1() which has chained async method calls. I'm really after how to use the async syntactic sugar to simplify this type of use case. How could I rewrite this block with the new syntax?
Future<String> test1() async {
test2().then((result) {
print(result);
test2().then((result) {
print(result);
});
});
// This should be executed immediately before
// the test2() chain completes.
print('hello');
return 'test1: done';
}
edit for follow up:
I am not sure what you want to do after all, but if you want wait for the chain to finish before printing things, here's what you have to do:
Future<String> test1() async {
String result;
var result1 = await test2();
print("Printing result1 $result1 ; this will wait for the first call to test2() to finish to print");
var result2 = await test2();
print("Printing result2 $result2 ; this will wait for the second call to test2() to print");
// This should be executed immediately before
// the test2() chain completes.
print('hello');
return 'test1: done';
}
Now if you call test1() and wait for it to return "test1: done" you have to await it.
main() async {
var result = await test1();
print(result); // should print "hello" and "test1: done"
}
If you want to execute code independently from a previous future result, just don't put the await keyword.
Future<String> test1() async {
test2().then((String result) => print(result)); // the call to test2 will not stop the flow of test1(). Once test2's execution will be finished, it'll print the result asynchronously
// This will be printed without having to wait for test2 to finish.
print('hello');
return 'test1: done';
}
The keyword await makes the flow stalled until test2() is finished.
This is because of the await before test2();. The execution is stalled until the Future returned by test2() is completed.

Resources