我通过在订阅后添加项目来修改教程https://www.dartlang.org/docs/tutorials/streams/中的一些示例:
import 'dart:async';
main() {
var data = new List<int>();
var stream = new Stream.fromIterable(data); // create the stream
// subscribe to the streams events
stream.listen((value) { //
print("Received: $value"); // onData handler
}); //
data.add(1);
}
在运行这个程序后,我得到了:
Uncaught Error: Concurrent modification during iteration: _GrowableList len:1.
Stack Trace:
#0 ListIterator.moveNext (dart:_collection-dev/iterable.dart:315)
#1 _IterablePendingEvents.handleNext (dart:async/stream_impl.dart:532)
#2 _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:661)
#3 _asyncRunCallback (dart:async/schedule_microtask.dart:18)
#4 _createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:11)
#5 _Timer._createTimerHandler._handleTimeout (timer_impl.dart:151)
#6 _Timer._createTimerHandler.<anonymous closure> (timer_impl.dart:166)
#7 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:93)
在添加侦听器之前放置data.add(1)
按预期工作。
我已经检查了documentation about Stream并且没有找到我做错的事。我期待听众会以最好的情况被解雇,并且在最坏的情况下不会被解雇,但也不例外。
这是预期的行为吗?如果是,请说明原因。
答案 0 :(得分:4)
异常来自您尝试在迭代时修改列表。这是Dart(*)中未指定的行为,并且使用的实现只是选择抛出异常。虽然它被Stream.fromIterable
中发生的异步事件混淆了,但它基本上和你试图这样做一样:
var data = [1,2,3];
for(var d in data) {
print(d);
data.add(d+10);
}
如果您将data.add
包裹在另一个异步调用中,例如Timer.run(() => data.add(2))
,则会“正常”。通过这个,我的意思是它不会抛出异常。
Received: 2
仍会不打印。该流只会发送调用new Stream.fromIterable
时列表中已有位置的元素。之后,流将关闭(将调用onDone
),并且不会将对原始列表的修改发送给您的侦听器。
(*)源代码:SDK 1.1.3中的iterator.dart
- “如果在迭代期间迭代了迭代的对象,则行为未指定。”为什么api.dartlang.org上的文字不同于我。
修改强>
回答评论中的问题:一种方法是使用StreamController
。
// or new StreamController<int>.broadcast(), if you want to listen to the stream more than once
StreamController s = new StreamController<int>();
// produce periodic errors
new Timer.periodic(new Duration(seconds: 5), (Timer t) {
s.isClosed ? t.cancel() : s.addError("I AM ERROR");
});
// add some elements before subscribing
s.add(6);
s.add(9);
// this will close the stream eventually
new Timer(new Duration(seconds: 20), () => s.close());
// start listening to the stream
s.stream.listen((v) => print(v),
onError: (err) => print("An error occured: $err"),
onDone: () => print("The stream was closed"));
// add another element before the next event loop iteration
Timer.run(() => s.add(4711));
// periodically add an element
new Timer.periodic(new Duration(seconds: 3), (Timer t) {
s.isClosed ? t.cancel() : s.add(0);
});
// one more (will be sent before 4711)
s.add(4);
答案 1 :(得分:0)
迭代时无法修改列表。 您需要一个没有此限制的迭代(例如自定义实现)。