无法将源自同一广播流的多个流添加到StreamController

时间:2019-08-21 10:20:45

标签: dart

我正在实现一个令牌生成器。它解析文档,在一组可能的定界符上对它进行标记,然后为我提供1、2和3字标记的组合。 我能够实现自己的目标,但是只能通过一种特定的方式进行:

  Stream<String> contentStr = file.openRead().transform(utf8.decoder);
  Stream<String> tokens = contentStr.transform(charSplitter).transform(tokenizer).asBroadcastStream();
  var twoWordTokens = tokens.transform(sliding(2));
  var threeWordTokens = tokens.transform(sliding(3));
  StreamController<String> merger = StreamController();
  tokens.forEach((token) => merger.add(token));
  threeWordTokens.forEach((token) => merger.add(token));
  twoWordTokens.forEach((token) => merger.add(token));
  merger.stream.forEach(print);

如您所见,我将执行以下操作:

  • 广播原始令牌流
  • 通过滑动窗口转换将其转换为2个其他流
  • 创建一个StreamConsumer(准确地说是StreamController),并将每个事件从3个流中抽取到该流的使用者。
  • 然后我打印流使用者的每个元素进行测试

它可以工作,但是我不喜欢通过StreamConsumer.add方法添加源流中的每个元素。我想改用StreamController.addStream,但是那不起作用。 以下代码给我一个Bad state: Cannot add event while adding a stream错误,我明白原因:

  StreamController<String> merger = StreamController();
  merger.addStream(tokens);
  merger.addStream(twoWordTokens);
  merger.addStream(threeWordTokens);
  merger.stream.forEach(print);

这是StreamController.addStream中的API documentation。 因此,我需要等待每个addStream返回将来的完成情况:

StreamController<String> merger = StreamController();
await merger.addStream(tokens);
await merger.addStream(twoWordTokens);
await merger.addStream(threeWordTokens);
await merger.stream.forEach(print);

但是在这种情况下,控制台上什么也没打印。

如果我这样做:

StreamController<String> merger = StreamController();
merger.stream.forEach(print);
await merger.addStream(tokens);
await merger.addStream(twoWordTokens);
await merger.addStream(threeWordTokens);

然后仅打印1字令牌,即原始广播流的元素。派生流的元素不是。

我有点理解为什么会这样,因为我的所有流都是从原始广播流派生的。

是否有更好的方法来实现这样的管道?

可能我的问题可以用流复制/分叉来重新表示,但是我看不到在Dart中克隆流的方法。如果您可以提供建议-请这样做。

1 个答案:

答案 0 :(得分:1)

我希望在某个时候允许并发addStream,但是在那之前,您需要独立处理事件:

var allAdds = [
    tokens.forEach(merger.add), 
    twoWordTokens.forEach(merger.add), 
    threeWordTokens.forEach(merger.add)]; 
Future.wait(allAdds).then((_) { merger.close(); });

merger.stream.forEach(print);

那是如果您想自己控制一切。您还可以使用package:async中的StreamGroup类。它收集大量流并将其事件作为单个流发出。

这假定您没有错误事件。