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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6229778/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 03:03:49  来源:igfitidea点击:

How do I get hold of exceptions thrown in a Scala Future?

scalaexception-handlingconcurrencyactor

提问by Duncan McGregor

I've been working up my answer to Is there a standard Scala function for running a block with a timeout?, and have run into a problem if an exception is thrown in a Future.

我一直在努力回答是否有一个标准的 Scala 函数来运行一个带有超时的块?,并且如果在 Future 中抛出异常就会遇到问题。

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

So that

以便

runWithTimeout(50) { "result" } should equal (Some("result"))
runWithTimeout(50) { Thread.sleep(100); "result" } should equal (None)

But if I throw an exception in my block it doesn't leak, but is swallowed - so that the following fails with "..no exception was thrown"

但是,如果我在我的块中抛出异常,它不会泄漏,而是被吞下 - 因此以下失败并显示“..没有抛出异常”

intercept[Exception] {
    runWithTimeout(50) { throw new Exception("deliberate") }
}.getMessage should equal("deliberate")

Syserr has a stack trace with the message

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

<function0>: caught java.lang.Exception: deliberate

but I can't find where in the Scala runtime that is printed.

但我在 Scala 运行时中找不到打印的位置。

Apart from wrapping f in another block which catches exceptions and propagates them if thrown, is there any way to persuade awaitAll and/or Future to throw?

除了将 f 包装在另一个捕获异常并在抛出时传播它们的块中,还有什么方法可以说服 awaitAll 和/或 Future 抛出?

采纳答案by Rex Kerr

Short answer: no.

简短的回答:没有。

Exceptions don't do what you want when you're working in a threaded context, because you want to know about the exception in the caller, and the exception happens in the future's thread.

当您在线程上下文中工作时,异常不会执行您想要的操作,因为您想知道调用者中的异常,而异常发生在未来的线程中。

Instead, if you want to know what the exception was, you should return an Either[Exception,WhatYouWant]--of course, you have to catch that exception within the future and package it up.

相反,如果您想知道异常是什么,您应该返回一个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")

回答by Viktor Klang

Disclaimer: I work for Typesafe

免责声明:我为 Typesafe 工作

Or.... you could use Akkaand it would give you what you want without you having to go through hoops for it.

或者……你可以使用Akka,它会为你提供你想要的东西,而你不必为此费心。

val f: Future[Int] = actor !!! message

Then

然后

    f.get 

Will throw the exception that happened in the actor

会抛出actor中发生的异常

    f.await.exception 

will give you an Option[Throwable]

会给你一个选项[Throwable]

回答by Eric Pabst

scala.concurrent.ops.futureincludes exception handling.

scala.concurrent.ops.future包括异常处理。

So, instead of importing scala.actors.Futures.future, import scala.concurrent.ops.future instead.

因此,不是scala.actors.Futures.futureimport ,而是import scala.concurrent.ops.future instead

That simple change in which import is there will cause the caller's call to .get to rethrow the exception. It works great!

导入的简单更改将导致调用者对 .get 的调用重新抛出异常。效果很好!

回答by Duncan McGregor

Working my way through @Rex Kerr's suggestion, I've created

通过@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)
    }
}

so that

以便

  @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")

}

Again, I'm a bit of a Scala novice, so would welcome feedback.

同样,我有点 Scala 新手,所以欢迎反馈。

回答by zainahamid

Or use Future.liftTryTry, turns it from Future[Object]to Future[Try[Object]], and you can match on the Try[Object]and check for an exception case Throw(e)and log / exit gracefully

或者使用Future.liftTryTry,将其从Future[Object]to Future[Try[Object]],您可以匹配Try[Object]并检查异常case Throw(e)并优雅地记录/退出

回答by IttayD

You need to override the method exceptionHandlerin order to catch exceptions. So your option is to define your own futuremethod so it creates a MyFutureActor with exceptionHandler.

您需要覆盖该方法exceptionHandler以捕获异常。所以你的选择是定义你自己的future方法,以便它创建一个带有异常处理程序的 MyFutureActor。

EDIT: FutureActor is private, so subclassing it isn't possible.

编辑:FutureActor 是私有的,因此子类化它是不可能的。

Another option is to use links to know when exceptions happened.

另一种选择是使用链接来了解异常发生的时间。

However, I think Rex Kerr's approach is better - just wrap the function in something that will catch the Exception. Too bad futuredoesn't already do that.

但是,我认为 Rex Kerr 的方法更好 - 只需将函数包装在可以捕获异常的东西中。太糟糕了future还没有这样做。