从stdout中删除流的正确方法是什么,以便可以添加另一个流?

时间:2015-05-22 17:25:31

标签: asynchronous stream dart stdout

我正在Dart中启动一个将stdout流附加到stdout的进程,以便可以将结果打印到终端,如下所示:

Process.start(executable, ['list','of','args']).then((proc) {
  stdout.addStream(proc.stdout);
  stderr.addStream(proc.stderr);
  return proc.exitCode;
});

然而,一旦完成,我想开始一个新的过程并再次开始(这个函数将被调用几次)。有时,我收到一个错误:

Uncaught Error: Bad State: StreamSink is already bound to a stream

查看dart文档,看起来我可能需要执行stdout.close()stdout.flush()之类的操作,但这些似乎无法解决问题。处理多个流顺序绑定到streamink的正确方法是什么?

3 个答案:

答案 0 :(得分:4)

addStream返回一个Future,指示何时完成流的添加。 addStream只应该有一个StreamSink同时出现的流。

根据您的需要/需要做什么,您现在有两个选择:

  • 将进程的输出多路复用到stdout。
  • 等待addStream完成。

后者更容易:

Process.start(executable, ['list','of','args']).then((proc) async {
  await stdout.addStream(proc.stdout);  // Edit: don't do this.
  await stdout.addStream(proc.stderr);
  return proc.exitCode;
});

请注意正文上的async修饰符以及正文中的两个await

编辑:不立即收听stderr是错误的。 (你的程序可以阻止它。)

如果您的程序输出足够小,您可以切换到Process.run

Process.run(executable, ['list','of','args']).then((procResult) {
  stdout.write(procResult.stdout);
  stdout.write(procResult.stderr);
  return procResult.exitCode;
});

但它不会交错stdout和stderr。

答案 1 :(得分:3)

您不能在同一个接收器上多次拨打addStream。 接收器处于“手动”模式或“自动”模式,后者通过添加流来触发。在添加完流之前,暂停将路由到正在添加的流而不是路由器,并且在完成之前不允许手动添加事件。好像添加的流接管了接收器,直到完成为止。

要将两个(或更多)流添加到交错的同一个接收器,您必须手动执行此操作。一种方法是使用一种通用的方式来交错流,如下面的代码。另一个选择是自己监听两个流并将事件添加到接收器:

Stream interleave(Iterable<Stream> streams) {
  List subscriptions = [];
  StreamController controller;
  controller = new StreamController(
    onListen: () {
      int active = 0;
      void done() {
        active--;
        if (active <= 0) controller.close();
      }
      for (var stream in streams) {
        active++;
        var sub = stream.listen(controller.add, 
                                onError: controller.addError,
                                onDone: done);
        subscriptions.add(sub);             
      }
    },
    onPause: () {
      for (var sub in subscriptions) { sub.pause(); }
    }, 
    onResume: () {
      for (var sub in subscriptions) { sub.resume(); }
    },
    onCancel: () {
      for (var sub in subscriptions) { sub.cancel(); }  
    }
  );
  return controller.stream;
}

(未经过彻底测试!)

答案 2 :(得分:0)

您也可以使用

StreamSubscription subscr = proc.stdout.listen(io.stdout.add);
...
subscr.cancel();

或使用可配置的处理程序

class StdOutHandler { 
  IOSink sink; 
  call(data) => sink.add(); 
}

void main() {
  var proc = await Process.start(...);
  var stdoutHandler = new StdOutHandler()..sink = stdout;
  var subscription = proc.stdout.listen(stdoutHandler);
  ....
  stdoutHandler.sink = ...
}