Julien
Julien

Reputation: 4163

Flutter - Dart : wait a forEach ends

I try to modify a string using data in each node I retrieve from Firebase database, and then to write a file with the modifed string (called "content").

Here is what I tried :

// Retrieve initial content from Firebase storage
var data = await FirebaseStorage.instance.ref().child("...").getData(1048576);
var content = new String.fromCharCodes(data);

// Edit content with each node from Firebase database
final response = await FirebaseDatabase.instance.reference().child('...').once();
response.value.forEach((jsonString) async {
     ...
     // cacheManager.getFile returns a Future<File>
     cacheManager.getFile(signedurl).then((file){ 
         // Modify content
         content=content.replaceAll('test',file.path);
     });
});

// Finally write the file with content
print("test");
final localfile = File('index.html');
await localfile.writeAsString(content);

Result :

"test" is shown before the forEach ends.

I found that we can do in Dart (https://groups.google.com/a/dartlang.org/forum/#!topic/misc/GHm2cKUxUDU) :

await Future.forEach

but in my case if I do : await Future.response.value.forEach (sounds a bit weird)

then I get :

Getter not found: 'response'. await Future.response.value.forEach((jsonString) async {

How to wait that forEach ends (with "content" modified) before to write the file with new content?

Any idea?

Upvotes: 6

Views: 23001

Answers (3)

Frank van Puffelen
Frank van Puffelen

Reputation: 599011

You defined the callback for forEach as async, which means that it runs asynchronously. In other words: the code inside of that callback runs independently of the code outside of the callback. That is exactly why print("test"); runs before the code inside of the callback.

The simplest solution is to move all code that needs information from within the callback into the callback. But there might also be a way to await all of the asynchronous callbacks, similar to how you already await the once call above it.

Update I got working what I think you want to do. With this JSON:

{
  "data" : {
    "key1" : {
      "created" : "20181221T072221",
      "name" : "I am key1"
    },
    "key2" : {
      "created" : "20181221T072258",
      "name" : "I am key 2"
    },
    "key3" : {
      "created" : "20181221T072310",
      "name" : "I am key 3"
    }
  },
  "index" : {
    "key1" : true,
    "key3" : true
  }
}

I can read the index, and then join the data with:

final ref = FirebaseDatabase.instance.reference().child("/53886373");
final index = await ref.child("index").once();
List<Future<DataSnapshot>> futures = [];
index.value.entries.forEach((json) async {
  print(json);
  futures.add(ref.child("data").child(json.key).once());
});
Future.wait(futures).then((List<DataSnapshot> responses) {
  responses.forEach((snapshot) {
    print(snapshot.value["name"]);
  });
});

Upvotes: 8

graphicbeacon
graphicbeacon

Reputation: 356

Have you tried:

File file = await cacheManager.getFile(signedurl);
content = content.replaceAll('test', file.path);

instead of:

cacheManager.getFile(signedurl).then((file){ ...});

EDIT:

Here's a fuller example trying to replicate what you have. I use a for loop instead of the forEach() method:

void main () async {
  List<String> str = await getFuture();
  print(str);
  var foo;

  for (var s in str) {
    var b = await Future(() => s);
    foo = b;
  }

  print('finish $foo');
}

getFuture() => Future(() => ['1', '2']); 

Upvotes: 0

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657396

If you use for(... in ) instead of forEach you can use async/await

Future someMethod() async {
  ...

  for(final jsonString in response.value) {
     ...
     // cacheManager.getFile returns a Future<File>
     cacheManager.getFile(signedurl).then((file){ 
         // Modify content
         content=content.replaceAll('test',file.path);
     });
  });
}

With forEach the calls fire away for each jsonString immediately and inside it await works, but forEach has return type void and that can't be awaited, only a Future can.

Upvotes: 18

Related Questions