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(); }); }); } });
forEach expects a synchronous function and won't do anything with the return value. It just calls the function and on to the next. for...of will actually await on the result of the execution of the function.
forEach is not designed for asynchronous code. (It was not suitable for promises, and it is not suitable for async-await.) For example, the following forEach loop might not do what it appears to do: const players = await this.
You can declare a synchronous function with async without an error. An async keyword would try to help you turn your function into asynchronous most of the time by enforcing the return type of your function to Future . You will get the following error if the return type is not Future .
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With