Je voudrais attendre un scala futur qui pourrait avoir échoué. Si j'utilise Await.result
L'exception sera levée. Au lieu de cela, si j'ai f: Future[String]
Je voudrait une méthode Await.resultOpt(f): Option[String]
ou Await.resultEither(f): Either[String]
.
Je pourrais l'obtenir en utilisant scala.util.control.Exception.catching
Ou je pourrais f map (Right(_)) recover { case t: Throwable => Left(t) }
, mais il doit y avoir un moyen plus simple.
Vous pouvez utiliser Await.ready
qui se bloque simplement jusqu'à ce que le futur ait réussi ou échoué, puis renvoie une référence à ce futur.
À partir de là, vous voudrez probablement obtenir le value
du futur, qui est un Option[Try[T]]
. En raison de Await.ready
, il devrait être sûr de supposer que le value
est un Some
. Ensuite, il suffit de mapper entre un Try[T]
Et un Either[Throwable, T]
.
La version courte:
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)
}
La version plus courte, juste pour promouvoir l'API:
scala> val f = Future(7)
f: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@13965637
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] = scala.concurrent.impl.Promise$DefaultPromise@64c4c1
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)
Il a un léger avantage à être un doublure.
Mais bien sûr, après avoir ajouté un .toEither
méthode d'extension, peu importe le nombre de lignes nécessaires.
Vous pouvez commencer à créer vos propres types d'utilitaires et à faire quelque chose comme ça
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))
}
Je l'ai séparé pour qu'un Try
ait également un .fold
:).