Как получить исключения, брошенные в Scala Будущее?
Я работал над своим ответом на Существует ли стандартная функция Scala для запуска блока с тайм-аутом? и возникла проблема, если исключение выбрано в будущем.
def runWithTimeout[T](timeoutMs: Long)(f: => T) : Option[T] = {
awaitAll(timeoutMs, future(f)).head.asInstanceOf[Option[T]]
}
Итак,
runWithTimeout(50) { "result" } should equal (Some("result"))
runWithTimeout(50) { Thread.sleep(100); "result" } should equal (None)
Но если я создаю исключение в своем блоке, он не просачивается, а проглатывается - так что следующее происходит с ошибкой ".. исключение исключено"
intercept[Exception] {
runWithTimeout(50) { throw new Exception("deliberate") }
}.getMessage should equal("deliberate")
Syserr имеет трассировку стека с сообщением
<function0>: caught java.lang.Exception: deliberate
но я не могу найти, где в Scala время выполнения, которое печатается.
Помимо обертывания f в другом блоке, который ловит исключения и распространяет их при броске, есть ли способ убедить awaitAll и/или Future бросить?
Ответы
Ответ 1
Короткий ответ: нет.
Исключения не делают то, что вы хотите, когда работаете в потоковом контексте, потому что хотите узнать об исключении в вызывающем, и исключение происходит в будущем потоке.
Вместо этого, если вы хотите знать, что такое исключение, вы должны вернуть 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")
Ответ 2
Отказ от ответственности: я работаю для Typesafe
Или... вы можете использовать Akka, и это даст вам то, что вы хотите, без необходимости проходить через обручи.
val f: Future[Int] = actor !!! message
Тогда
f.get
Выбросит исключение, которое произошло в акторе
f.await.exception
предоставит вам опцию [Throwable]
Ответ 3
Работая с моим предложением @Rex Kerr, я создал
object Timeout {
val timeoutException = new TimeoutException
def runWithTimeout[T](timeoutMs: Long)(f: => T) : Either[Throwable, T] = {
runWithTimeoutIgnoreExceptions(timeoutMs)(exceptionOrResult(f)) match {
case Some(x) => x
case None => Left(timeoutException)
}
}
def runWithTimeout[T](timeoutMs: Long, default: T)(f: => T) : Either[Throwable, T] = {
val defaultAsEither: Either[Throwable, T] = Right(default)
runWithTimeoutIgnoreExceptions(timeoutMs, defaultAsEither)(exceptionOrResult(f))
}
def runWithTimeoutIgnoreExceptions[T](timeoutMs: Long)(f: => T) : Option[T] = {
awaitAll(timeoutMs, future(f)).head.asInstanceOf[Option[T]]
}
def runWithTimeoutIgnoreExceptions[T](timeoutMs: Long, default: T)(f: => T) : T = {
runWithTimeoutIgnoreExceptions(timeoutMs)(f).getOrElse(default)
}
private def exceptionOrResult[T](f: => T): Either[Throwable, T] =
try {
Right(f)
} catch {
case x => Left(x)
}
}
так что
@Test def test_exception {
runWithTimeout(50) { "result" }.right.get should be ("result")
runWithTimeout(50) { throw new Exception("deliberate") }.left.get.getMessage should be ("deliberate")
runWithTimeout(50) { Thread.sleep(100); "result" }.left.get should be (Timeout.timeoutException)
runWithTimeout(50, "no result") { "result" }.right.get should be ("result")
runWithTimeout(50, "no result") { throw new Exception("deliberate") }.left.get.getMessage should be ("deliberate")
runWithTimeout(50, "no result") { Thread.sleep(100); "result" }.right.get should be ("no result")
}
Опять же, я немного новичок Scala, так что бы приветствовать обратную связь.
Ответ 4
scala.concurrent.ops.future
включает обработку исключений.
Итак, вместо импорта scala.actors.Futures.future
, импортируйте scala.concurrent.ops.future instead
.
Это простое изменение, в котором происходит импорт, вызовет вызов вызывающего абонента .get, чтобы восстановить исключение. Он отлично работает!
Ответ 5
Или используйте Future.liftTryTry
, превращая его из Future[Object]
в Future[Try[Object]]
, и вы можете сопоставлять его с Try[Object]
и проверять исключение case Throw(e)
и изящно лог/выйти
Ответ 6
Для исключения исключений вам необходимо переопределить метод exceptionHandler
. Таким образом, ваш вариант состоит в том, чтобы определить ваш собственный метод future
, чтобы он создавал MyFutureActor с исключениемHandler.
EDIT: FutureActor является приватным, поэтому подклассификация невозможна.
Другой вариант - использовать ссылки, чтобы знать, когда произошли исключения.
Однако я думаю, что подход Рекса Керра лучше - просто оберните функцию в нечто, что поймает Исключение. Слишком плохо future
этого не делает.