如何在fs2中“拆分”流?

时间:2019-12-09 18:36:14

标签: scala fs2

我想做这样的事情:

def splitStream[F, A](stream: fs2.Stream[F, A], split: A => B): (Stream[F, A], Stream[F, B)) = 
  (stream, stream.map(split)

但是这不起作用,因为它两次从源中“拉出”-当我同时耗尽streamstream.map(split)时一次。我该如何预防?通过维护自己的内部缓冲区以某种方式切换到基于“推送”的模型,这样我就不会拉两次?

1 个答案:

答案 0 :(得分:2)

  

通过维护自己的内部缓冲区以某种方式切换到基于“推送”的模型,这样我就不会拉两次了?

是的。例如,您可以使用来自fs2的队列:

def splitStream[F[_], A](stream: Stream[F, A], split: A => B): F[(Stream[F, A], Stream[F, B])] = 
  for {
    q <- Queue.noneTerminated[F, A]
  } yield (stream.evalTap(a => q.enqueue1(Some(a)).onFinalize(q.enqueue1(None)), q.dequeue.map(split))

当然,这里的问题是,如果调用方忽略其中任何一个流,则另一个将死锁并且从不发出任何东西。通常,这是您在尝试将流分成多个流时遇到的问题,并且无论其何时订阅,其值都保证出现在每个子流中。

我通常寻求的解决方案是合并较大的动作并使用broadcastparJoin之类的运算符:

def splitAndRun[F[_]: Concurrent, A](
  base: Stream[F, A],
  runSeveralThings: List[Stream[F, A] => Stream[F, Unit]]
): F[Unit] =
  base.broadcastTo(run: _*).compile.drain

在这里,您知道将要有多少个消费者,因此一开始就不会有被忽略的流。