Akka Reactive Streams总是留下一条消息

时间:2016-07-10 16:26:28

标签: scala akka akka-stream reactive-streams

出于某种原因,我的Akka流总是等待第二条消息,然后发出"(?)第一条消息。

以下是一些演示我问题的示例代码。

val rx = Source((1 to 100).toStream.map { t =>
  Thread.sleep(1000)
  println(s"doing $t")
  t
})
rx.runForeach(println)

产出输出:

doing 1
doing 2
1
doing 3
2
doing 4
3
doing 5
4
doing 6
5
...

我想要的是什么:

doing 1
1
doing 2
2
doing 3
3
doing 4
4
doing 5
5
doing 6
6
...

2 个答案:

答案 0 :(得分:5)

现在设置代码的方式,在允许开始向下游发送元素之前,您正在完全转换Source。通过删除代表源的数字范围的toStream,您可以清楚地看到该行为(如@slouc所述)。如果你这样做,你会看到Source在开始响应下游需求之前先完全转换。如果您确实想要在Source中运行Sink并在中间进行转换步骤,那么您可以尝试构建如下内容:

val transform =
  Flow[Int].map{ t =>
    Thread.sleep(1000)
    println(s"doing $t")
    t
  }

Source((1 to 100).toStream).
  via(transform ).
  to(Sink.foreach(println)).
  run

如果进行了更改,那么您将获得所需的效果,即在下一个元素开始处理之前,流向下游的元素将一直处理整个流程。

答案 1 :(得分:1)

您正在使用.toStream(),这意味着整个集合都是懒惰的。如果没有它,你的输出将首先是一百个" s" s后面跟着从1到100的数字。但是,Stream只评估第一个元素,这给出了"做1&# 34;输出,它停止的地方。下一个元素将在需要时进行评估。

现在,我无法在文档中找到关于此的任何细节,但我认为runForeach有一个实现,它在调用当前函数之前接受下一个元素。因此,在元素n上调用println之前,它首先检查元素n + 1(例如检查它是否存在),这导致"做n + 1"信息。然后它在当前元素上执行println函数,从而产生消息" n" 。

你真的需要在map()之前runForeach吗?我的意思是,您需要通过数据进行两次旅行吗?我知道我可能会说明显而易见的事情,但如果你只是像这样处理你的数据:

val rx = Source((1 to 100).toStream)
rx.runForeach({ t =>
  Thread.sleep(1000)
  println(s"doing $t")
  // do something with 't', which is now equal to what "doing" says
})

然后你不会在什么时候评估什么。