如何掌握Scala Future中抛出的异常?

Dun*_*gor 15 concurrency scala exception-handling actor

我一直在努力回答是否有一个标准的Scala函数用于运行超时的块?,如果在Future中抛出异常,则会遇到问题.

  def runWithTimeout[T](timeoutMs: Long)(f: => T) : Option[T] = {
    awaitAll(timeoutMs, future(f)).head.asInstanceOf[Option[T]]
  }
Run Code Online (Sandbox Code Playgroud)

以便

runWithTimeout(50) { "result" } should equal (Some("result"))
runWithTimeout(50) { Thread.sleep(100); "result" } should equal (None)
Run Code Online (Sandbox Code Playgroud)

但是如果我在我的块中抛出一个异常它不会泄漏,但是被吞下 - 所以下面的代码失败了"..没有抛出异常"

intercept[Exception] {
    runWithTimeout(50) { throw new Exception("deliberate") }
}.getMessage should equal("deliberate")
Run Code Online (Sandbox Code Playgroud)

Syserr有一条带有消息的堆栈跟踪

<function0>: caught java.lang.Exception: deliberate
Run Code Online (Sandbox Code Playgroud)

但我无法找到Scala运行时中打印的位置.

除了在另一个块中包装f以捕获异常并在抛出时传播它们,有没有办法说服awaitAll和/或Future抛出?

Rex*_*err 14

简答:不.

当您在线程上下文中工作时,异常不会执行您想要的操作,因为您希望了解调用者中的异常,并且异常发生在将来的线程中.

相反,如果你想知道异常是什么,你应该返回Either[Exception,WhatYouWant]- 当然,你必须在将来捕获该异常并将其打包.

scala> scala.actors.Futures.future{
  try { Right("fail".toInt) } catch { case e: Exception => Left(e) }
}
res0: scala.actors.Future[Product with Serializable with Either[Exception,Int]] = <function0>

scala> res0()   // Apply the future
res1: Product with Serializable with Either[Exception,Int] =
      Left(java.lang.NumberFormatException: For input string: "fail")
Run Code Online (Sandbox Code Playgroud)


Vik*_*ang 10

免责声明:我为Typesafe工作

或者....你可以使用Akka,它可以给你你想要的东西,而不必为它完成箍.

val f: Future[Int] = actor !!! message
Run Code Online (Sandbox Code Playgroud)

然后

    f.get 
Run Code Online (Sandbox Code Playgroud)

将抛出actor中发生的异常

    f.await.exception 
Run Code Online (Sandbox Code Playgroud)

会给你一个选项[Throwable]