我正在scala中编写一个递归重试函数,我想知道将来创建是否存在运行时错误。如果有,那么将重试未来的实例化。
想象一下,我有一个数据库查询功能:
dbLookup(userId : UserId) : Option[UserName] = ???
重试会看起来像某种形式:
retry[T](f : () => Future[Option[T]],
notifyFailure : (t : Throwable) => Unit,
n : Int) : Future[Option[T]] = {
if(n <= 0) { Future{None} }
else {
val fut = f()
if(f.resultsInException) { //I don't know how to write this
notifyFailure(f.exception)
retry(f, notifyFailure, n-1) //try again
}
else {
f.originalValueAsFuture
}
}
}
如何实现此未来功能并允许尾递归优化?
如果执行上下文在我尝试创建Future时不断抛出异常,则此函数可用于为用户重试数据库10次:
val userNameAfterRetries =
retry(() => Future{dbLookup("1234")},
(t) => system error (s"future creation error : $t"),
10)
注意:Future.fallbackTo
可能会有这种情况,但遗憾的是,fallbackTo会引用Future[T]
而不是() => Future[T]
。这很重要,因为即使第一次尝试成功,使用fallbackTo也会导致重试至少1次。
提前感谢您的考虑和回应。
答案 0 :(得分:4)
这个怎么样?
def retry[T](f: () => Future[Option[T]],
notifyFailure: Throwable => Unit,
n: Int)(implicit ec : ExecutionContext): Future[Option[T]] = {
if (n <= 0) Future.failed(new RuntimeException("Exceeded number of allowed retries"))
else f().recoverWith { case originalError => notifyFailure(originalError); retry(f, notifyFailure, n - 1) }
}
尾递归的更新:Future
的性质是异步的,所以除非你想await
得到结果,否则我觉得不可能做到@tailrec
{ {1}}因为你必须在回调中使用递归。
同样实用的说明:如果你知道它总是重复10次,我就不会害怕递归。