为什么这个懒惰的迭代会不止一次地解析?

时间:2017-10-31 22:49:51

标签: asynchronous promise dart lazy-evaluation

我今天用一些生产代码遇到了这个并且能够用一个简单的.toList()修复它以在等待之前解析lazyList,但我不明白为什么它以这种方式工作而仅< / em>使用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)

1 个答案:

答案 0 :(得分:4)

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

映射的iterable的每次迭代都将重新迭代原始的iterable并再次执行map操作,这意味着它是一个惰性转换。对于像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* */);
});

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

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

使用toList强制评估iterable的每个映射元素,从而消除惰性。