2014-01-28 64 views
26

我想等待可能失败的scala未来。如果我使用Await.result,则会引发异常。相反,如果我有f: Future[String]我想要一个方法Await.resultOpt(f): Option[String]Await.resultEither(f): Either[String]等待未来,收到

我可以通过使用scala.util.control.Exception.catching或我可以f map (Right(_)) recover { case t: Throwable => Left(t) },但必须有一个更直接的方法。

+2

'Try' /'Success' /'Failure'比'Either [Throwable,Value]'要好得多。 IMO ... –

回答

54

您可以使用Await.ready,它只是阻止,直到未来成功或失败,然后返回一个引用回到该未来。

从那里,你可能会想要得到未来的value,这是一个Option[Try[T]]。由于拨打Await.ready,应该可以安全地假设valueSome。然后,这只是Try[T]Either[Throwable, T]之间的映射问题。

短的版本:

val f: Future[T] = ... 

val result: Try[T] = Await.ready(f, Duration.Inf).value.get 

val resultEither = result match { 
    case Success(t) => Right(t) 
    case Failure(e) => Left(e) 
} 
+0

我觉得这是更好的解决方案: 'Await.ready(F,Duration.Inf).onComplete(结果=> { 结果匹配{ 成功的情况下(T)=>右键(T) 情况失败(e)=>左(e) } }) 调用** get ** on选项通常是一个坏主意 – Klapsa2503

+0

@ Klapsa2503在大多数情况下,我会同意,但在这种情况下,它是完全正常的;如果'Await.ready'没有抛出,'value'将永远是'Some'。一个可以自由添加额外的检查代码质量的原因,但这在技术上应该是不必要的。 – Dylan

+0

@Dylan - 你为什么使用'等待#ready'而不是'waitwait#result'? –

3

较短的版本,只是为了推广API:

scala> val f = Future(7) 
f: scala.concurrent.Future[Int] = [email protected] 

scala> f.value.get 
res0: scala.util.Try[Int] = Success(7) 

scala> import scala.util._ 
import scala.util._ 

scala> Either.cond(res0.isSuccess, res0.get, res0.failed.get) 
res2: scala.util.Either[Throwable,Int] = Right(7) 

scala> val f = Future[Int](???) 
f: scala.concurrent.Future[Int] = [email protected] 

scala> val v = f.value.get 
v: scala.util.Try[Int] = Failure(java.util.concurrent.ExecutionException: Boxed Error) 

scala> Either.cond(v.isSuccess, v.get, v.failed.get) 
res4: scala.util.Either[Throwable,Int] = Left(java.util.concurrent.ExecutionException: Boxed Error) 

,它无意成为一个班轮略占优势。

但当然,在添加.toEither扩展方法之后,您并不在乎它需要多少行。

+0

如果未来尚未完成,这将无法正常工作。 'f.value'将是None,因此调用'f.value.get'将会抛出一个异常。 – Dylan

+0

@Dylan我只是在res2展示'Either.cond'。显然,必须等待它。编辑:哦,你是迪伦。是的,你的比赛很好,这是一个单线,不一定是明智的。再次编辑:如果您翻译过,那么敏锐也无所谓。 –

0

你可以开始做自己的类型utils的,做一些像这样

trait RichTypes { 

    import scala.util.{Try, Success, Failure} 
    import scala.concurrent.{Await, Future} 
    import scala.concurrent.duration.Duration 

    implicit class RichFuture[T](f: Future[T]) { 
     def awaitResult(d: Duration): Either[Throwable, T] = { 
     Try(Await.result(f, d)).toEither 
     } 
    } 

    implicit class RichTry[T](tri: Try[T]) { 
     def toEither(): Either[Throwable, T] = { 
     tri.fold[Either[Throwable, T]](Left(_), Right(_)) 
     } 
    } 
    } 

    object Example 
    extends App 
     with RichTypes { 

    import scala.concurrent.Future 
    import scala.concurrent.duration._ 

    val succ = Future.successful("hi").awaitResult(5.seconds) 
    val fail = Future.failed(new Exception("x")).awaitResult(5.seconds) 

    println(succ) // Right(hi) 
    println(fail) // Left(Exception(x)) 
    } 

我分离出来的Try也有​​:)。