23

是否可以链接 scala.util.Try 和 scala.concurrent.Future?它们都有效地提供了相同的单子接口,但试图链接它们会导致编译错误。

例如。鉴于下面的两个签名

def someFuture:Future[String] = ???
def processResult(value:String):Try[String] = ???

是否可以执行以下操作?

val result = for( a <- someFuture; b <- processResult( a ) ) yield b;
result.map { /* Success Block */ } recover { /* Failure Block */ }

这显然会导致编译错误,因为 Future 和 Try 无法一起进行 flatMapp'ed。

然而,能够链接它们将是一个很好的功能 - 这可能吗?还是我需要将它们组合成 Future[Try[String]]?

(特别是,我有兴趣拥有一个“恢复”块来捕获将来尝试中的异常)。

4

5 回答 5

28

当遇到这样的问题时,您想在理解中使用不同的类型,一种解决方案可以是尝试选择其中一种类型并将另一种类型映射到它。对于您的情况,鉴于期货的独特属性(异步),我会选择Future作为最低公分母Try并将Future. 你可以简单地这样做:

val result = for{
  a <- someFuture
  b <- tryToFuture(processResult(a)) 
}  yield b
result.map { /* Success Block */ } recover { /* Failure Block */ }

def tryToFuture[T](t:Try[T]):Future[T] = {
  t match{
    case Success(s) => Future.successful(s)
    case Failure(ex) => Future.failed(ex)
  }
}

现在,如果您发现这是一种非常常见的情况,并且您不喜欢不断地添加显式转换,我想您可以将tryToFuture方法定义为某个辅助对象上的隐式并在需要的地方导入它,如下所示:

object FutureHelpers{
  implicit def tryToFuture[T](t:Try[T]):Future[T] = {
    t match{
      case Success(s) => Future.successful(s)
      case Failure(ex) => Future.failed(ex)
    }
  }
}

import FutureHelpers._
val result = for{
  a <- someFuture
  b <- processResult(a) 
}  yield b
result.map { /* Success Block */ } recover { /* Failure Block */ }

请记住,调用Future.success并对范围内的Future.failed任何内容产生影响ExecutionContext,因为它将在后台向其提交另一个任务。

编辑

正如 Viktor 在评论中指出的那样,如果您在下面的更新示例中使用 like,则将 a 转换Try为 a的过程会更加容易:FutureFuture.fromTry

val result = for{
  a <- someFuture
  b <- Future.fromTry(processResult(a)) 
}  yield b
result.map { /* Success Block */ } recover { /* Failure Block */ }

这可能是你最好的选择,而不是使用隐式或滚动你自己的转换逻辑。

于 2013-07-28T12:09:38.070 回答
2

怎么样

val result = for( a <- someFuture) yield for( b <- processResult( a ) ) yield b;

虽然看起来不整齐。

于 2014-01-08T16:05:52.273 回答
2

也许问题很老,但目前您可以:

  implicit def tryToFuture[T](t:Try[T]):Future[T] = Promise.fromTry(t).future
于 2016-05-26T00:03:33.823 回答
1
  implicit def convFuture[T](ft: Future[Try[T]]): Future[T] =
ft.flatMap {
  _ match {
    case Success(s) => Future.successful(s)
    case Failure(f) => Future.failed(f)
  }
}
于 2014-06-24T23:29:57.637 回答
1

还有

Future.fromTry(Try { ... })

所以你可以做

 val result = for {
   a <- someFuture
   b <- Future.fromTry(processResult(a)) 
 } yield b;
于 2016-08-25T14:48:34.867 回答