【问题标题】:Await a future, receive an either等待一个未来,得到一个
【发布时间】:2014-02-20 10:38:45
【问题描述】:

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

我可以使用scala.util.control.Exception.catchingf map (Right(_)) recover { case t: Throwable => Left(t) } 来获得这个,但必须有更直接的方法。

【问题讨论】:

  • Try / Success / FailureEither[Throwable, Value] 要好得多。海事组织...

标签: scala future


【解决方案1】:

您可以使用 Await.ready 简单地阻塞直到 Future 成功或失败,然后返回对该 Future 的引用。

从那里,您可能想要获取 Future 的 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)
}

【讨论】:

  • 我认为这是更好的解决方案:Await.ready(f, Duration.Inf).onComplete(result => { result match { case Success(t) => Right(t) case Failure(e) => Left(e) } }) 在 Option 上调用 get 通常是个坏主意
  • @Klapsa2503 在大多数情况下我会同意,但在这种情况下完全没问题;如果Await.ready 没有抛出,value 将永远是Some。出于代码质量原因,可以免费添加额外的检查,但这里在技术上应该是不必要的。
  • @Dylan - 你为什么用Await#ready而不是Await#result
  • @KevinMeredith,因为 OP 特别指出了Await.ready 的问题,并要求提供替代方案。
  • @KevinMeredith(哎呀,意思是“Await.result 的问题”)
【解决方案2】:

较短的版本,只是为了推广 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)

单线有一点优势。

当然,在添加.toEither扩展方法之后,你并不关心它花了多少行。

【讨论】:

  • 如果 Future 还没有完成,这将不起作用。 f.value 将为 None,因此调用 f.value.get 将引发异常。
  • @Dylan 我只是在 res2 演示 Either.cond。显然,人们必须等待它。编辑:哦,你就是那个迪伦。是的,你的比赛很好,这是一个单线,不一定是敏锐的。再次编辑:如果你拉皮条,洞察力也无关紧要。
【解决方案3】:

你可以开始制作自己的类型工具并做类似的事情

  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 也有一个.fold :)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-29
    • 2017-10-03
    • 1970-01-01
    • 2020-07-05
    • 1970-01-01
    • 2018-03-30
    • 2020-05-17
    • 1970-01-01
    相关资源
    最近更新 更多