具有未来返回类型的Scala递归函数

时间:2016-10-21 13:29:42

标签: scala concurrency

我正在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次。

提前感谢您的考虑和回应。

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次,我就不会害怕递归。