如何多次重复等待?

How to repeat Await multiple times?

我是 Scala 的新手。 我只想在超时时重试 code1。如果再出现错误,我会换种方式处理。如果超时,我想再试一次,比如3次。这样做的正确方法是什么?

我不确定使用 code2 之类的东西是否是在 Scala 上执行此操作的最佳方式。也许已经有更好的东西了。有没有更清洁的破解方法?我应该使用 Await.ready 而不是结果吗?

我也不确定我是否应该使用 Await.ready 或 Await.result。

代码 1:

Await.result(myMethod(), Duration(120, "seconds"))

代码 2:

breakable {
  for(_ <- 0 to 3) {
    Try(Await.result(myMethod(), Duration(120, "seconds"))) match {
      case Success(value) => {
        // do something
        break()
      }
       case Failure(_) => // how can I now if it is timeout?
    }
  }
}

您可以使用递归来实现简单的重试机制。匹配 TimeoutException 失败将使您能够专门处理超时情况。

下面是一个例子,假设 myMethod returns 一个 Future[String]:

def myMethod(): Future[String] = ???

val maxAttempts = 3

def retryOnTimeout(attempt: Int = 0): String = {
  Try(Await.result(myMethod(), Duration(120, "seconds"))) match {
    case Success(result) => result
    case Failure(_: TimeoutException) if attempt < maxAttempts => retryOnTimeout(attempt  + 1)
    case Failure(err) => throw err
  }
}

retryOnTimeout 可以变得更通用,允许它在多个地方使用,函数作为参数传入。

等待 Future 会使整个 Future 变得毫无意义。也就是说,应该处理 3 种情况:

  1. 所有尝试都超时
  2. 一个non-time-out失败
  3. a Future 成功完成
import scala.concurrent.duration._
import scala.concurrent.{Future, Await, TimeoutException}
import scala.util.{Try, Success, Failure}

//            v--max number of attempts
LazyList.fill(4)(Try(Await.result(myMethod(), 120.seconds)))
  .dropWhile{case Failure(_:TimeoutException) => true
             case _ => false
  }.headOption.fold{
    //every attempt timed out
  }{
    case Success(value) => //do something
    case Failure(ex:Throwable) => //ex is non-timeout exception
  }

注意:LazyList 是 Scala 2.13.x 集合。列表中的每个元素只被评估 if/when 它是必需的,所以如果第一个成功,那么 none 其他期货将被启动。

您可以尝试将以下方法应用于 repeating evaluation

下面的代码片段定义了一个通用的 retry 方法:这个方法接受一个参数,在 try - catch 块中对其进行计算,并且 re-executes 它在失败时最大数量尝试。

retry 接受 by-name parameter 是允许它在必要时重复计算 myMethod block 的原因。

object Test {

  def retry[T](max: Int)(f: => T): T = {
    var tries = 0
    var result: Option[T] = None
    while (result == None) {
      try {
        result = Some(f)
      }
      catch {
        case e: Throwable => tries += 1
        if (tries > max) throw e
        else {
          println(s"failed, retry #$tries")
        }
      }
    }
    result.get
  }

  def myMethod(n: Int): Int = {
    val num = scala.util.Random.nextInt(10)
    if(n < num) throw new Exception
    else num
  }

  def main(args: Array[String]): Unit = {
    println(retry(max = 5){myMethod(3)})
/*
failed, retry #1
failed, retry #2
3
*/
    println(retry(max = 5){myMethod(-1)})
/*
failed, retry #1
failed, retry #2
failed, retry #3
failed, retry #4
failed, retry #5
Exception in thread "main" java.lang.Exception
*/
  }
}

注意 myMethod 函数可以采用新的参数超时来为每个函数调用引入一些时间限制。

  def myMethod(n: Int, timeout: Long): Int = {
    var num = scala.util.Random.nextInt(10)

    val current = System.currentTimeMillis()
    var running = System.currentTimeMillis()
    while (running - current <= timeout && n < num) {
      num = scala.util.Random.nextInt(10)
      running = System.currentTimeMillis()
    }
    println(s"time goes on ${running - current}")
    if(n < num) throw new Exception
    else num
  }

// retry function doesn't change
println(retry(5){myMethod(5,5000/*five seconds*/)})
/*
time goes on 5001
failed, retry #1
time goes on 5001
1
*/
println(retry(5){myMethod(-1,5000/*five seconds*/)})
/*
time goes on 5001
failed, retry #1
time goes on 5001
failed, retry #2
time goes on 5001
failed, retry #3
time goes on 5001
failed, retry #4
time goes on 5001
failed, retry #5
time goes on 5001
Exception in thread "main" java.lang.Exception
*/