如何在Dart中使用Async/await在List.forEach()函数中实现异步操作

106

我正在编写一种机器人(命令行应用程序),当我使用“forEach”方法时,我在异步执行方面遇到了麻烦。 这是我尝试做的简化代码:

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}");
}

这里是输出结果:

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

我想得到的是:

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

如果有人知道我做错了什么,我会很感激。


2
异步调用与<List>.forEach()不兼容,您需要使用传统循环。为什么?这篇文章解释得非常清楚。我刚刚发现Dart和Node.JS几乎相同。 - Ουιλιαμ Αρκευα
7个回答

284

您需要使用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}");
}

41
这应该是被批准的答案。 - Grahambo
5
注意,这仅适用于可迭代对象。 - skytect
1
是的,这正是我需要的完美解决方案,因为应用程序存在互联网连接问题。它将加载列表并添加内容,当完成时进行通知。 - mike-gallego
在asyncTwo方法中,无法使用Future.delayed。输出是:main start asyncOne start asyncOne end main end asyncTwo #1 asyncTwo #2 asyncTwo #3。 - Mangesh Kadam
2
请注意,虽然这是最佳方法,但编译器可能不知道块内num的类型。因此,您可能需要调用类似于await Future.forEach<int>(someIntList, (item) async {的内容。 - David Chopin

79

我认为使用forEach方法无法实现你想要的功能。但是使用for循环可以实现。例如:

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

这正是我开始思考的。谢谢您的确认。我曾以为"await"表达式会优先于其周围的任何内容,但嗯... - aelayeb

18

因为forEach不会查看回调函数的返回值,所以无法用它来处理这个问题。如果回调函数返回的是future对象,那么它们将会被丢失并且不会被等待。

你可以像Steven Upton建议的那样使用循环,或者如果你想让操作同时运行而不是一个接一个运行,可以使用Future.wait

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

12

我知道这是一个老问题,但我会留下一个新答案,希望这能帮助未来的某个人。

你可以使用forEach 来实现你想要达成的目标,像这样做:

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

3
那实际上就是@Stephane之前发布的内容的一个子集。 - Keith DC

2

即使您已创建自定义类列表,也请使用forEach循环。

    lol() async {
  await Future.forEach(myListofFood,(Food food) async {
    await secondaryfuncton(food);
  });
}

2
如果您需要迭代Map,则可以使用以下方法:
await Future.forEach(myMap.keys, (key) async {
  final value = data[key];
}

1

这种解决方案的优点是,它可以与mapIndexed一起使用。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接