为什么加入流时Dart StreamController.addStream无法正常工作

时间:2013-09-05 23:11:13

标签: dart dart-async

鉴于以下Dart代码段:

Stream stream1 = new Stream.periodic(new Duration(seconds: 1), (n) => n)
                           .take(10)
                           .asBroadcastStream();
stream1.listen((n) => print("stream1 : $n"),
               onError : (err) => print("stream1 : $err"),
               onDone : () => print("stream1 : done"),
               cancelOnError : false);

Stream stream2 = stream1.where((n) => n % 2 == 0).take(2);
stream2.listen((n) => print("stream2 : $n"),
               onError : (err) => print("stream2 : $err"),
               onDone : () => print("stream2 : done"),
               cancelOnError : false);

Stream stream3 = stream1.where((n) => n % 2 != 0).take(2);
stream3.listen((n) => print("stream3 : $n"),
               onError : (err) => print("stream3 : $err"),
               onDone : () => print("stream3 : done"),
               cancelOnError : false);

StreamController controller = new StreamController.broadcast();
controller.addStream(stream2)
  .then((_) => controller.addStream(stream3));
controller.stream.listen((n) => print("composite stream : $n"),
                         onError : (err) => print("composite stream : $err"),
                         onDone : () => print("composite stream : done"),
                         cancelOnError : false);

我得到以下输出:

stream1 : 0
stream2 : 0
composite stream : 0
stream1 : 1
stream3 : 1
composite stream : 1
stream1 : 2
stream1 : 3
stream1 : 4
stream1 : 5
stream1 : 6
stream1 : 7
stream1 : 8
stream1 : 9
stream1 : done
stream2 : done
stream3 : done

从这个输出中我有一些我不理解的事情:

  1. 为什么stream2stream3每个应该只有2个?我使用StreamController创建的复合流是否会使用stream2stream3中的一个事件?这种行为对我来说似乎很奇怪,我错过了什么?

  2. 为什么stream2stream3仅在stream1完成时完成?当这两者都有限时,这并不是我所期望的自然行为,而且与.take(10) stream1的行为相矛盾。如果我删除.take(10)上的stream1,则stream2stream3实际上永远不会完成。

  3. 如果我修改controller以添加源stream1(请参阅下面的代码段和输出),那么stream2stream3实际上在其自然位置完成他们的2个元素已经启动,但我还得到了一个例外,因为它试图两次收听其中一个流。

    StreamController controller = new StreamController.broadcast();
    controller.addStream(stream1)
      .then((_) => controller.addStream(stream2))
      .then((_) => controller.addStream(stream3));
    controller.stream.listen((n) => print("composite stream : $n"),
                             onError : (err) => print("composite stream : $err"),
                             onDone : () => print("composite stream : done"),
                             cancelOnError : false);
    
    stream1 : 0
    stream2 : 0
    composite stream : 0
    stream1 : 1
    stream3 : 1
    composite stream : 1
    stream1 : 2
    stream2 : 2
    stream2 : done
    composite stream : 2
    stream1 : 3
    stream3 : 3
    stream3 : done
    composite stream : 3
    stream1 : 4
    composite stream : 4
    stream1 : 5
    composite stream : 5
    stream1 : 6
    composite stream : 6
    stream1 : 7
    composite stream : 7
    stream1 : 8
    composite stream : 8
    stream1 : 9
    stream1 : done
    composite stream : 9
    Uncaught Error: Bad state: Stream has already been listened to.
    Stack Trace:
    #0      _StreamController._subscribe (dart:async/stream_controller.dart:151:7)
    #1      _ControllerStream._createSubscription (dart:async/stream_controller.dart:259:157)
    #2      _StreamImpl.listen (dart:async/stream_impl.dart:260:58)
    #3      _ForwardingStreamSubscription._ForwardingStreamSubscription (dart:async/stream_pipe.dart:53:43)
    #4      _ForwardingStream._createSubscription (dart:async/stream_pipe.dart:35:16)
    #5      _ForwardingStream.listen (dart:async/stream_pipe.dart:32:31)
    #6      _AsBroadcastStream.listen (dart:async/stream_impl.dart:466:37)
    #7      _ForwardingStreamSubscription._ForwardingStreamSubscription (dart:async/stream_pipe.dart:53:43)
    #8      _ForwardingStream._createSubscription (dart:async/stream_pipe.dart:35:16)
    #9      _ForwardingStream.listen (dart:async/stream_pipe.dart:32:31)
    #10     _ForwardingStreamSubscription._ForwardingStreamSubscription (dart:async/stream_pipe.dart:53:43)
    #11     _ForwardingStream._createSubscription (dart:async/stream_pipe.dart:35:16)
    #12     _ForwardingStream.listen (dart:async/stream_pipe.dart:32:31)
    #13     _AddStreamState._AddStreamState (dart:async/stream_controller.dart:300:133)
    #14     _BroadcastStreamController.addStream (dart:async/broadcast_stream_controller.dart:140:27)
    #15     main.<anonymous closure> (file:///C:/SourceCode/personal/SteamTest/lib/streamtest.dart:38:38)
    #16     _ThenFuture._zonedSendValue (dart:async/future_impl.dart:371:24)
    #17     _TransformFuture._sendValue.<anonymous closure> (dart:async/future_impl.dart:348:48)
    #18     _ZoneBase._runInZone (dart:async/zone.dart:82:17)
    #19     _ZoneBase._runUnguarded (dart:async/zone.dart:102:22)
    #20     _ZoneBase.executeCallback (dart:async/zone.dart:58:23)
    #21     _TransformFuture._sendValue (dart:async/future_impl.dart:348:26)
    #22     _FutureImpl._setValueUnchecked (dart:async/future_impl.dart:184:26)
    #23     _FutureImpl._asyncSetValue.<anonymous closure> (dart:async/future_impl.dart:218:25)
    #24     _asyncRunCallback (dart:async/event_loop.dart:9:15)
    #25     _createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:8:13)
    #26     _Timer._createTimerHandler._handleTimeout (timer_impl.dart:95:21)
    #27     _Timer._createTimerHandler.<anonymous closure> (timer_impl.dart:111:23)
    #28     _ReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:81:92)
    
    
    Unhandled exception:
    Bad state: Stream has already been listened to.
    #0      _DefaultZone.handleUncaughtError.<anonymous closure> (dart:async/zone.dart:146:7)
    #1      _asyncRunCallback (dart:async/event_loop.dart:9:15)
    #2      _asyncRunCallback (dart:async/event_loop.dart:13:7)
    #3      _createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:8:13)
    #4      _Timer._createTimerHandler._handleTimeout (timer_impl.dart:95:21)
    #5      _Timer._createTimerHandler._handleTimeout (timer_impl.dart:103:7)
    #6      _Timer._createTimerHandler._handleTimeout (timer_impl.dart:103:7)
    #7      _Timer._createTimerHandler.<anonymous closure> (timer_impl.dart:111:23)
    #8      _ReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:81:92)
    

    有人可以帮我理解这里发生的事情吗?

    谢谢,

1 个答案:

答案 0 :(得分:3)

在为您的示例调试dart代码之后,在我看来这是一个错误。

接听调用生成一个Take-stream,它引用了一个引用broadcastStream stream1的Where-stream。顺便说一下,因为stream1是一个broadcastStream,所以它也适用于Where-和Take-stream(在调试期间检查)。

事件数量的计数器是Take-stream的状态变量,但是每次订阅Take-Stream(通过调用listen)时,这会创建一个对Where-stream的订阅,然后依次创建一个订阅stream1。在这种情况下,您有2个订阅stream2(1个通过listen,1个通过控制器),导致两个2订阅stream1(因为stream3总共4个,但这对讨论来说并不重要)。

因此,当stream1触发事件0时,它会在stream2中通过Take-stream两次传递,减少Take-stream的计数器两次,并导致onDone事件仅传递给第二个订阅。因为计数器减少2并且它是Take-stream的状态变量,所以当stream1触发事件2时,Take-Stream将不再触发。

所以这看起来像是一个错误。两个观察结果。

  1. 接收流计数器是否应该是对接收流的订阅而不是接收流本身的状态变量?

  2. 每次订阅Take-Stream都会导致对源流的新订阅吗? (另请参阅Dart代码中的ForwardingStream)。这可能取决于广播环境。

  3. 也许他们应该进一步了解Rx代码。