Scala期货和`andThen`异常传播

时间:2016-10-18 17:49:59

标签: java scala exception exception-handling future

我在scala.concurrent.Future模块中阅读了andThen函数的Scala 2.11.8文档,并说明了以下内容:

def andThen[U](pf: PartialFunction[Try[T], U])
              (implicit executor: ExecutionContext): Future[T]
  

将副作用函数应用于此未来的结果,并且   这个未来的结果将带来一个新的未来。

     

此方法允许强制执行回调在a中执行   指定的顺序。

     

请注意,如果其中一个链接的andThen回调引发异常,   该异常不会传播到后续的andThen回调。   相反,后续的andThen回调将被赋予原始值   这个未来。

我不确定andThen不会传播异常的确切含义是什么,也没有提供示例。例如,如果我这样做:

Future.successful { throw new RuntimeException("test") } andThen
                  { case _ => println("test") }

在Scala REPL中,我得到:

java.lang.RuntimeException: test
  ... 32 elided

因此传播了异常。有人可以提供一个有意义的例子,这究竟意味着什么,以及使用andThen代码是否安全,我抛出了我想从中恢复的异常。谢谢。

2 个答案:

答案 0 :(得分:3)

throw中不要Future.successful {}例外。

这是正确的方法

Future { throw new RuntimeException("test") } andThen
                  { case _ => println("test") }

您可以使用以下代码行

了解andThen的使用情况
Future.successful { 1 } andThen { case _ =>  "foo" }

<强> REPL

@ Future.successful { 1 } andThen { case _ =>  "foo" }
res7: Future[Int] = Success(1)

<强> REPL

@ Future.successful { 1 } andThen { case _ =>  println("foo") }
foo
res8: Future[Int] = Success(1)

<强> REPL

@ val result = Future.successful { 1 } andThen { case _ =>  "foo" }
result: Future[Int] = Success(1)

在上面的例子中

我们可以看到执行了andhhen之后的部分函数,​​但忽略了部分函数返回类型。最后,结果输出是Future的结果,即Future[Int]

这意味着addThen用于在Future完成后执行副作用功能。

当未来是失败

<强> REPL

@ val result = Future { throw new Exception("bar") } andThen { case _ =>  "foo" }
result: Future[Nothing] = Failure(java.lang.Exception: bar)

<强> REPL

@ val result = Future { throw new Exception("bar") } andThen { case _ =>  println("foo") }
foo
result: Future[Nothing] = Failure(java.lang.Exception: bar)

未来失败的情况也是如此。然后执行代码,然后执行,但是在byThen之后的代码结果被忽略,最终结果是Future结果。

因此,andThen用于在Future完成后立即运行副作用代码。 andThen也将最终输出保持为Future的输出。

这是andThen在标准库中的实现方式。

andThen位于Future

 def andThen[U](pf: PartialFunction[Try[T], U])(implicit executor: ExecutionContext): Future[T] = {
    val p = Promise[T]()
    onComplete {
      case r => try pf.applyOrElse[Try[T], Any](r, Predef.conforms[Try[T]]) finally p complete r
    }
    p.future
  }
  

1)将副作用函数应用于此未来的结果,并返回具有此未来结果的新未来。

pf是副作用代码,因为它的输出类型未被使用(不能使用)。 p.future是他正在谈论的新未来。 Promise已完成上一个Future结果(请查看上面addThen的实施情况)

finally块p complete r中的

表示使用p.future创建新的Future,并使用之前的r

结果完成
  

2)此方法允许强制执行回调以指定顺序执行。

是。在前一个未来完成后执行pf。查看代码pf是在onComplete块内执行的。

  

3)请注意,如果链接的andThen回调之一抛出异常,则该异常不会传播到后续的andThen回调。相反,后续的andThen回调将被赋予此未来的原始值。

r这是前一个未来的结果,是pf(请查看上面的andThen代码)

答案 1 :(得分:1)

我认为类型签名是最好的文档。 如您所见,andThen接受T => U(省略PF并尝试简化),并返回Future[T]。 所以你可以想到它,andThen执行一些效果并返回原始未来。因此,如果您的部分函数提升异常,它将不会传播到其他andThen并且它们将对原始未来起作用:

  import scala.concurrent.ExecutionContext.Implicits.global
  Future{ 2 } andThen {
    case _ => throw new RuntimeException("test")
  } andThen {
    case v ⇒ println("After exception"); println(v)
  }
Thread.sleep(500)

打印:

java.lang.RuntimeException: test    
After exception
Success(2)

PS。再次阅读您的示例。我想你最后忘记了Thread.sleep,以便在程序结束之前完成未来。所以你可能正确地理解了一切。