为什么这个懒惰的迭代器解决了不止一次?

KOG*_*OGI 1 asynchronous lazy-evaluation promise dart

我今天用一些生产代码遇到了这个问题,并且能够.toList()在等待之前通过一个简单的解决lazyList来修复它,但我不明白为什么它会以这种方式工作并且在使用Future.wait()这里发生的事情时?为什么 lazyList 会被解析两次?

在 DartPad 上玩它(更改第doWait3 行的值并查看不同的结果)


代码

import 'dart:async';

void main() {
  var executedTracker  = [];
  var source           = ["a", "b", "c"];

  List promises = source.map((item) async {
    print('executing item $item${(executedTracker.contains(item) ? ' (again!? o_O)' : '')}'); executedTracker.add(item);
    return (item*2);
  });

  Future.wait(promises).whenComplete(() {
    print('--------\nAll promises complete.');
    print('Processing ${promises.length} results...\n');
    promises.forEach((promise) => null /* do a thing with the result*/);
  });
}
Run Code Online (Sandbox Code Playgroud)

输出

executing item a
executing item b
executing item c

All promises complete.
Processing 3 results...

executing item a (again!? o_O)
executing item b (again!? o_O)
executing item c (again!? o_O)
Run Code Online (Sandbox Code Playgroud)

lrn*_*lrn 5

因为你迭代了promises两次,一次是Future.wait(promises)promises.forEach(...). (你很幸运promises.length- 因为映射的可迭代对象知道它基于一个列表,它不会再次迭代以找到长度。)

映射的迭代器的每次迭代都会重新迭代原始迭代器并再次执行映射操作,这就是惰性转换的含义。对于像这样的情况,懒惰是必要的hugelyGiganticIterable.map(something).take(10).toList()。如果它不是懒惰,它将对巨大的可迭代对象中的所有元素执行映射(它甚至可以是无限的,可迭代对象可以是无限的,与列表不同)。

在实际示例中,您可能想要做的是使用Future.wait(promises)操作的结果:

Future.wait(promises).then((items) {
  print('--------\nAll promises complete.');
  print('Processing ${items.length} results...\n');
  items.forEach((item) => null /* do a thing with the *result* */);
});
Run Code Online (Sandbox Code Playgroud)

如果您实际上不想要懒惰的行为,那么您应该急切地收集值。你这样做,例如,通过写:

List promises = source.map((item) async {
  ...
}).toList();  // <-- notice the ".toList()"!
Run Code Online (Sandbox Code Playgroud)

UsingtoList强制对可迭代对象的每个映射元素进行评估,从而消除惰性。