我有一个分页资源,我想用Monix递归地使用它。我想要一个Observable,它将发出下载的元素并递归地使用页面。这是一个简单的例子。它当然不起作用。它会发出第一页,然后是第一页+第二页,然后是第一页+第二页+第三页。我希望它先发射,然后发射,然后发射第三,依此类推。
object Main extends App {
sealed trait Event
case class Loaded(xs: Seq[String]) extends Event
// probably should just finish stream instead of this event
case object Done extends Event
// here is the problem
def consume(page: Int, size: Int):Observable[Event] = {
Observable.fromFuture(getPaginatedResource(page, size)).concatMap{ xs =>
if (xs.isEmpty) Observable.pure(Done)
else Observable.concat(Observable.pure(Loaded(xs)), consume(page + 1, size + 5))
}
}
def getPaginatedResource(page: Int, size: Int):Future[Seq[String]] = Future {
if (page * size > 100) Seq.empty
else 0 to size map (x => s"element $x")
}
consume(page = 0, size = 5).foreach(println)
}
有什么想法吗?
UPD
对不起,它似乎正在运行,我只是有一个错误size + 5
。所以问题似乎已经解决了,但如果你发现我做错了什么,请告诉我。
答案 0 :(得分:1)
通常建议尽可能在每次使用Observable
时避免递归。因为它不容易可视化并且通常更容易出错。
一个想法是使用scanEvalF
,因为它会在每个步骤中发出项目。
sealed trait Event
object Event {
case class Loaded(page: Int, size: Int, items: Seq[String]) extends Event
}
def getPaginatedResource(page: Int, size: Int): Task[Loaded] = Task.pure {
if (page * size > 100) Loaded(page, size, Seq.empty)
else Loaded(page, size, 0.to(size).map(x => s"element $x"))
}
def consume(page: Int, size: Int): Observable[Event] = {
Observable
.interval(0.seconds)
.scanEvalF(getPaginatedResource(page, size)) { (xs, _) =>
getPaginatedResource(xs.page + 1, xs.size + 5)
} // will emit items on each step
.takeWhileInclusive(_.items.nonEmpty) // only take until list is empty
}
consume(0, 5)
.foreachL(println)
.runToFuture