104

I'm writing some kind of bot (command line application) and I'm having trouble with async execution when I'm using the "forEach" method. Here is a simplified code of what I'm trying to do :

main() async {
  print("main start");
  await asyncOne();
  print("main end");
}

asyncOne() async {
  print("asyncOne start");
  [1, 2, 3].forEach(await (num) async {
    await asyncTwo(num);
  });
  print("asyncOne end");
}

asyncTwo(num) async
{
  print("asyncTwo #${num}");
}

And here is the output :

main start
asyncOne start
asyncOne end
main end
asyncTwo #1
asyncTwo #2
asyncTwo #3

What I'm trying to get is :

main start
asyncOne start
asyncTwo #1
asyncTwo #2
asyncTwo #3
asyncOne end
main end

If someone knows what I'm doing wrong I would appreciate it.

aelayeb
  • 1,208
  • 2
  • 9
  • 11
  • 2
    Asynchronous calls don't work with `.forEach()`, you need to use traditional loops. Why? [This article](https://www.coreycleary.me/why-does-async-await-in-a-foreach-not-actually-await/) explains it very well. I just discovered that Dart and Node.JS are almost the same. – Ουιλιαμ Αρκευα Sep 09 '20 at 03:48

7 Answers7

282

You need to use Future.forEach.

main() async {
  print("main start");
  await asyncOne();
  print("main end");
}

asyncOne() async {
  print("asyncOne start");
  await Future.forEach([1, 2, 3], (num) async {
    await asyncTwo(num);
  });
  print("asyncOne end");
}

asyncTwo(num) async
{
  print("asyncTwo #${num}");
}
Stephane
  • 11,056
  • 9
  • 41
  • 51
  • 41
    This should be the approved answer. – Grahambo Dec 12 '18 at 16:59
  • 5
    For anyone reading this, do note that this unfortunately only works for iterables. – skytect Jun 28 '19 at 09:50
  • 1
    Yesss this is so perfect on what I needed because of internet connection issues with the app. It'll load the list and add it, when it is complete, notify. – mike-gallego Aug 19 '19 at 20:47
  • this does not work with Future.delayed in asyncTwo method. output is main start asyncOne start asyncOne end main end asyncTwo #1 asyncTwo #2 asyncTwo #3 – Mangesh Kadam Mar 20 '20 at 13:09
  • 2
    Note that while this is the best approach, the compiler may not know the type of `num` within the block. So you may have to call something like `await Future.forEach(someIntList, (item) async {` – David Chopin Dec 17 '21 at 22:43
78

I don't think it's possible to achieve what you want with the forEach method. However it will work with a for loop. Example;

asyncOne() async {
  print("asyncOne start");
  for (num number in [1, 2, 3])
    await asyncTwo(number);
  print("asyncOne end");
}
stwupton
  • 2,177
  • 17
  • 18
  • That's what I was starting to think. Thank you for confirming it. I thought that "await" expression precedes whatever is surrounding it but oh well... – aelayeb Feb 26 '17 at 11:24
18

You can't use forEach for this because it doesn't actually look at the return values of its callbacks. If they are futures, they will just be lost and not awaited.

You can either do a loop like Steven Upton suggested, or you can use Future.wait if you want the operations to run simultaneously, not one after the other:

asyncOne() async {
  print("asyncOne start");
  await Future.wait([1, 2, 3].map(asyncTwo));
  print("asyncOne end");
}
lrn
  • 64,680
  • 7
  • 105
  • 121
12

I know this is an old question, but I'll leave here a new answer, hoping this help someone in the future.

You can use forEach for what you're trying to achieve by doing something like this:

  asyncOne() async {
  print("asyncOne start");
  await Future.forEach([1, 2, 3],(num) async {
    await asyncTwo(num);
  });
  print("asyncOne end");
}
TmRocha
  • 155
  • 1
  • 5
2

use forEach loop even if you have created a list of custom class.

    lol() async {
  await Future.forEach(myListofFood,(Food food) async {
    await secondaryfuncton(food);
  });
}
The VOYOU
  • 522
  • 4
  • 14
zuri
  • 75
  • 3
1

If you need iterate a Map, you can use:

await Future.forEach(myMap.keys, (key) async {
  final value = data[key];
}
Álvaro Agüero
  • 4,494
  • 1
  • 42
  • 39
1

The advantage of this solution, is that it can be used with mapIndexed

await Future.wait([1, 2, 3].map(  // or mapIndexed(
  (num) async {
    await asyncTwo(num);
  }));