My async call is returning before list is populated in forEach loop
Solution 1
This code
Future<List<String>> readHeaderData() async {
List<String> l = new List();
List<String> files = await readHeaders(); // Gets filenames
files.forEach((filename) async {
final file = await File(filename);
String contents = await file.readAsString();
User user = User.fromJson(json.decode(contents));
String name = user.NameLast + ", " + user.NameFirst;
print(name);
l.add(name);
}
return l;
}
returns the list l
and then processes the asyc forEach(...)
callbacks
If you change it to
Future<List<String>> readHeaderData() async {
List<String> l = new List();
List<String> files = await readHeaders(); // Gets filenames
for(var filename in files) { /// <<<<==== changed line
final file = await File(filename);
String contents = await file.readAsString();
User user = User.fromJson(json.decode(contents));
String name = user.NameLast + ", " + user.NameFirst;
print(name);
l.add(name);
}
return l;
}
the function will not return before all filenames are processed.
files.forEach((filename) async {
means that you can use await
inside the callback, but forEach
doesn't care about what (filename) async {...}
returns.
Solution 2
Also possible
await Future.forEach(yourList, (T elem) async { ...async staff });
Solution 3
To expand on Günter's comment regarding using list.map(f)
, here's an example of converting a forEach
call so that it works correctly.
Broken example
Incorrectly assumes forEach
will wait on futures:
Future<void> brokenExample(List<String> someInput) async {
List<String> results;
someInput.forEach((input) async {
String result = await doSomethingAsync(input);
results.add(result);
});
return results;
}
Corrected example
Waits on the async functions to complete, using Future.wait
and .map()
:
Future<void> correctedExample(List<String> someInput) async {
List<String> results;
await Future.wait(someInput.map((input) async {
String result = await doSomethingAsync(input);
results.add(result);
}));
return results;
}
Jerry
This is the stackExchange, if you really are curious, then ask... I am sure that someone has an answer...
Updated on June 25, 2022Comments
-
Jerry almost 2 years
I have a routine which gets a list of filenames from the device, then reads the file(s) to build a list. However, the calling routine always returns with zero items. I print the filenames, so I know they exist, however, it appears that the async is returning before I read the files. I used similar code when making an HTTP call. But, something here is causing the routine to return the list even though it hasn't completed. Perhaps, it is possible that I am calling it at the wrong time? I am calling retrieveItems here:
@override void initState() { super.initState(); retrieveItems(); }
Eventually I will have a refresh button, but for now I'd simply like the list to populate with the data from the files...
--------------------
Callee
Future<List<String>> readHeaderData() async { List<String> l = new List(); List<String> files = await readHeaders(); // Gets filenames files.forEach((filename) async { final file = await File(filename); String contents = await file.readAsString(); User usr = User.fromJson(json.decode(contents)); String name = usr.NameLast + ", " + usr.NameFirst; print(name); l.add(name); } return l;
Caller
void retrieveItems() async { LocalStorage storage = new LocalStorage(); await storage.readHeaderData().then((item) { try { if ((item != null ) &&(item.length >= 1)) { setState(() { users.clear(); _users.addAll(item); }); } else { setState(() { _users.clear(); final snackbar = new SnackBar( content: new Text('No users found.'), ); scaffoldKey.currentState.showSnackBar(snackbar); }); } } on FileNotFoundException catch (e) { print(e.toString()); //For debug only setState(() { _users.clear(); }); }); } });