为什么这个惰性迭代解析不止一次?

Why is this lazy iterable resolving more than once?

我 运行 今天用一些生产代码解决了这个问题,并且能够用一个简单的 .toList() 修复它,在等待之前解析 lazyList,但我不明白为什么它会这样工作并且 使用 Future.wait() 时这是怎么回事?为什么 lazyList 被解析了两次?

Play with it on DartPad(改变第3行doWait的值,看到不同的结果)


代码

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*/);
  });
}

输出

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)

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

一个mapped iterable的每次迭代都会重新迭代原来的iterable并再次执行map操作,这就是lazy transformation的意思。对于hugelyGiganticIterable.map(something).take(10).toList()这样的情况,懒惰是必要的。如果它不是惰性的,它将对巨大的 iterable 中的所有元素执行映射(它甚至可以是无限的,iterables 可以是无限的,与列表不同)。

您可能想在真实示例中使用 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* */);
});

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

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

使用toList 强制计算可迭代对象的每个映射元素,消除惰性。