Scala 未来递归模式/任意长度的未来链接
我很好奇递归构建Akka futures链的最佳方法,该链将按顺序运行,如果未来的Scala 未来递归模式/任意长度的未来链接,scala,recursion,akka,future,Scala,Recursion,Akka,Future,我很好奇递归构建Akka futures链的最佳方法,该链将按顺序运行,如果未来的doWork调用失败,未来最多应重试3次,如果重试次数不足,该链应失败。假设所有doWork调用都通过了返回的futurefutChain应该只完成 object Main extends App { val futChain = recurse(2) def recurse(param: Int, retries: Int = 3): Future[String] { Future {
doWork
调用失败,未来最多应重试3次,如果重试次数不足,该链应失败。假设所有doWork
调用都通过了返回的futurefutChain
应该只完成
object Main extends App {
val futChain = recurse(2)
def recurse(param: Int, retries: Int = 3): Future[String] {
Future {
doWorkThatMayFailReturningString(param...)
} recoverWith {
case e =>
if (retries > 0) recurse(param, retries -1)
else Future.failed(e)
} flatMap {
strRes => recurse(nextParam) //how should the res from the previous fut be passed?
}
}
futChain onComplete {
case res => println(res) //should print all the strings
}
}
String
从doWork
函数返回(我需要以某种方式修改recurse
func以返回Futrue[List[String]
recover
还是recoverWith
flatMap
链接这些调用可以吗您可以实现如下可重试的
未来:
def retry[T](f: => Future[T])(n: Int)(implicit e: ExecutionContext): Future[T] = {
n match {
case i if (i > 1) => f.recoverWith{ case t: Throwable => retry(f)(n - 1)}
case _ => f
}
}
这并不是针对尾部递归进行优化的,但是如果您只想重试几次,就不会出现堆栈溢出(我想,如果前几次失败了,那么无论如何它都会继续失败)
然后我将分别进行链接。如果要链接在一起的函数数量有限,则每个函数都取决于前面的函数(出于某种原因,您希望聚合结果),您可以使用进行理解(语法糖用于flatMap
):
这将解开从List[Future[String]]
到Future[List[String]]
的问题
下面是一种按顺序执行类似操作的方法:
def sequential[A, B](seq: List[A])(f: A => Future[B])(implicit e: ExecutionContext): Future[List[B]] = {
seq.foldLeft(Future.successful(List[B]())) { case (left, next) =>
left.flatMap(list => f(next).map(_ :: list))
}
}
def doWork(param: String): String = ...
val results: Future[List[String]] = sequential(parameters)(param => Future(doWork(param)))
这些函数的实现对您的用例非常敏感。如果链中的任何一个期货失败,上述两个函数将返回失败的期货。有时您会希望这样,有时则不会。如果您只希望收集成功的期货,并放弃失败的期货而不使整个结果失败,则可以添加额外的step以恢复故障
此外,recover
和recoverWith
之间的区别在于它接受的PartialFunction
的类型。recover
用默认值替换失败的期货,而recoverWith
则使用另一个Future
进行替换。在我的重试
的情况下,recoverWith
是的,我理解重试语义。问题实际上是关于如何实现任意链。您能否提供如何实现arb链的示例。使用上面的理解方法与recursion@NightWolf你应该做些什么您的问题更清楚。将重试和将来的链接实现分开也很好。感谢您的更新!这是一个很好的方法,与此类似,但问题是这不是真正的递归,因为我需要在开始时定义参数集。为什么它需要递归?我的上一个示例使用预定义ed参数集,所以我不太确定您在寻找什么。这个问题归结为如何使用递归创建任意长的未来链。不仅仅是在一个用于理解的foldLeft
中插入一堆预定义的未来,还可以用于创建动态大小的future
@RandallSchulz的链。您愿意吗我想举个例子吗?是的,但是我必须等到星期一回到办公室。
def doWork(param: String): String = ...
val parameters: List[String] = List(...)
val results: Future[List[String]] = Future.sequence(parameters.map(doWork(_)))
def sequential[A, B](seq: List[A])(f: A => Future[B])(implicit e: ExecutionContext): Future[List[B]] = {
seq.foldLeft(Future.successful(List[B]())) { case (left, next) =>
left.flatMap(list => f(next).map(_ :: list))
}
}
def doWork(param: String): String = ...
val results: Future[List[String]] = sequential(parameters)(param => Future(doWork(param)))