将Try转换为Future并使用recoverWith作为Future。

11
我有一个抛出异常的Try,我想将其转换为Future,以便我可以使用recoverWith。如何在不处理Try中的任何异常的情况下将其转换为Future(只需在Future中进行恢复)?请注意,需要使用Await来测试您的future。以下是示例代码,但它也会在到达时抛出异常(我得到的是new RuntimeException("-------failed-------")):
val t = Try(throw new RuntimeException("my"))

val resF : Future[String] = if (t.isSuccess)
  Future.successful(t.get)
else
  Future.failed(new RuntimeException("-------failed-------"))

val resFWithRecover = resF.recoverWith{
  case NonFatal(e) =>
    Future.successful("recoveredWith")
}
Await.result(resFWithRecover, Duration("5s"))
5个回答

25
如何在不处理任何异常的情况下将 Try 转换为 Future?使用Future.fromTry
scala> val t = Try(throw new RuntimeException("my"))
t: scala.util.Try[Nothing] = Failure(java.lang.RuntimeException: my)

scala> val resF = Future.fromTry(t)
resF: scala.concurrent.Future[Nothing] = scala.concurrent.impl.Promise$KeptPromise@57cf54e1

scala> resF.recoverWith{
     |   case NonFatal(e) =>
     |     Future.successful("recoveredWith")
     | }
res5: scala.concurrent.Future[String] = scala.concurrent.impl.Promise$DefaultPromise@1b75c2e3

执行以下代码:val res = Await.result(resF, Duration("1s")),将抛出异常。 - ozma
1
@ozma,不要在resF上使用Await。相反,等待recoverWith返回的内容。换句话说,val resRW = resF.recoverWith{...},然后Await.result(resRW, ... - jwvh
@jwvh 这就是我所做的(现在已将其添加到代码示例中) - ozma
@ozma,你的代码示例没有使用m-z建议的fromTry()。我的测试表明m-z的代码是有效的。你只需要在适当的Future上等待,即不是从fromTry()返回的那个,而是从fromTry()之后返回的recoverWith()函数**。 - jwvh
@ozma首先,如果你只是打算使用Await来阻塞,那么使用Future有什么用呢?这没有意义。其次,如果你要使用Await,你需要在最后一个Future上这样做,也就是调用recoverWith的结果。在我的例子中,你会在res5上等待,而不是resF。即Await.result(resF.recoverWith { ... }, Duration.Inf)。 Futures是不可变的。恢复resF不会改变resF,它会创建一个新的Future - Michael Zajac
显示剩余2条评论

1
如果你只想在Try对象上使用recoverWith(类似于flatMap),那就不需要介绍Future了。
你可以像下面这样做:
val t = Try[String](throw new RuntimeException("my"))
val u = t.recoverWith{
  case e => Success(s"ignoring exception ${e.getLocalizedMessage}")
}
u.foreach(println(_))

这将导致以下内容输出到控制台:
ignoring exception my

谢谢,但那不是我的问题。我需要保留一个返回Future的接口,并且它需要像一个Future一样运作。 - ozma

1
 // you need to provide your try with type information in lhs
 // as the rhs is not providing any type info
 val t: Try[String] = Try(throw new RuntimeException("my"))

 // Now you can easily get a Future[String] from this Try[String]
 val f = Future.fromTry(t)

 // or you can use pattern matching
 val f2 = t match {
   case Success(str) => Future.succesful(str)
   case Failure(ex) => Future.failed(ex)
 }

我猜你的意思是 "f2 = f match {..."。但这段代码在我的环境中无法编译通过。请提供完整的示例代码和正确的导入语句。谢谢。 - ozma

0

你也可以在 Try 上使用 recoverWith

使用 Try 上的 maprecover 方法分别生成 Future.successfulFuture.failed,然后在 Try 上调用 get

val future = 
 Try {
   throw new Exception("explosion")
 }.map { result =>
   Future.successful(result)
 }.recover { case th =>
   Future.failed(th)
 }.get

使用模式匹配

val future =  
 Try {
  throw new Exception("something")
 } match {
  case Success(value) => Future.successful(value)
  case Failure(th) => Future.failed(th)
 }

请执行“doSomething()”,它会抛出一个异常,并且你无法通过“.get”方法获得它。 - ozma
谢谢。现在将“recoverWith”和“Await”添加到该future中,您仍将拥有异常而不是已恢复的结果。 - ozma

0

Future.fromTry会同步运行Try代码。来自文档:

创建一个已经完成的Future,其中包含指定的结果或异常。

由于您的接口依赖于Future,因此您可能希望异步运行Try本身。对于大多数用例,这将更加高效。您可以像这样做:

val f = Future { 
  val t = Try(...)
  t match {
    case Success(value) => value
    case Failure(exception) => throw exception
  }
}

f.recoverWith{...}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接