在Scala中最简单的超时方法是什么?

时间:2015-01-16 16:26:45

标签: scala timeout future

关于期货与期货的结合有很多问题。说实话,我还没有完全理解如何使用它们。但似乎我偶然发现了一个我必须(或许不是)的问题。

如果语句超过1分钟,我想抛出一个TimeoutException。要更清楚,目前,此语句尝试从服务器获取响应,但如果服务器未设置则不会抛出。它目前看起来像这样:

//proper import of exceptions
case class ServerException(exception: Throwable) extends Exception(exception)
//Code that instantiates client and post
val response = try {
  client.execute(post)
} catch {
  case e@(_: IOException | _: ClientProtocolException) => throw new ServerException(e)
}

为了缓解这个问题,我想引入一个超时。如何在此语句中引入超时,以便在一分钟内没有响应时抛出,否则它实例化response并且程序继续保持原样?

2 个答案:

答案 0 :(得分:0)

scala Futures不具备此功能。你可以切换到scalaz任务 - 它对于异步/延迟计算有点不同的抽象。您可以在此处阅读有关它的精彩文档:http://timperrett.com/2014/07/20/scalaz-task-the-missing-documentation/

  import java.util.concurrent.Executors
  import scalaz.concurrent.Task
  import scala.concurrent.duration._

  implicit val scheduledThreadPool = 
    Executors.newScheduledThreadPool(5)

  def executeRequest(req: Request): Task[Response] = ???

  val withTimeOut: Task[Response] = 
    executeRequest(req).timed(1.minute)

<强>更新

顺便说一下,您可以轻松地将Future转换为Task,例如Future来自第三方lib

object Future2Task {

  implicit class Transformer[+T](fut: => Future[T]) {
    def toTask(implicit ec: scala.concurrent.ExecutionContext): Task[T] = {
      import scala.util.{Failure, Success}
      import scalaz.syntax.either._
      Task.async {
        register =>
          fut.onComplete {
            case Success(v) => register(v.right)
            case Failure(ex) => register(ex.left)
          }
      }
    }
  }    
}

答案 1 :(得分:0)

超时通常通过将异步计时器作为超时信号并在计时器完成时完成将来的任务来实现。

我相信Akka有一个这样的计时器,但推出自己的计时器非常简单:

object ConcurrencyUtil {

  // creates a Future that will complete after a specified duration
  object Delay {
    def apply(d: Duration): Future[Unit] = {
      val p = Promise[Unit]()
      val t = new Timer
      t.schedule(new TimerTask {
        override def run(): Unit = p.success()
      }, d.toMillis)
      p.future
    }
  }

  implicit class FutureExtensions[T](future: Future[T]) {

    def timeout(timeout: Duration) = Future.firstCompletedOf(Seq(
      Delay(timeout).map(_ => throw new TimeoutException()),
      future
    ))
  }
}

现在你可以像以下一样为你的未来撰写timeout

import ConcurrencyUtil._
val f = someTaskReturningAFuture.timeout(1.minute)

现在,如果任务未在1分钟内完成,则延迟将触发,映射为抛出TimeoutException并完成将来f失败。

注意:这不会解决取消,即另一个未来,而不再被听取将继续存在,如果它正在执行某些事情,继续执行。