出于某种原因,我的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
...
答案 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
})
然后你不会在什么时候评估什么。