反转 Scala 的未来

Invert a Scala Future

是否有可能 'invert' Scala 的未来?

有时 Future 的结果是成功意味着错误。在那种情况下,翻转 Future 会很好,即调用一个 returns Future 的函数,如果原始 Future 失败,它会以指定的值成功,如果原始 Future 成功,则失败并返回指定的错误。

def flip[T](original: Future[T])(value: => T)(error: Throwable): Future[T] = ???
  def craziness[A](future: Future[A])(default : => A)(error: Throwable)(implicit ec: ExecutionContext): Future[A] = {
    val p = Promise[A]()
    import scala.util.{Success, Failure, Try}
    future.onComplete {
      case _: Success[_] => p.failure(error)
      case _: Failure[_] => p.complete(Try(default))
    }
    p.future
  }

这是一个显示它有效的 repl 会话:

scala> val f1 = craziness[String](Future("hello!"))("my crazy default")(new Throwable("boom"))
f1: scala.concurrent.Future[String] = scala.concurrent.impl.Promise$DefaultPromise@4d154ccd

scala> f1 onComplete { println }
Failure(java.lang.Throwable: boom)

scala> val f2 = craziness[String](Future(throw new Exception("boom!")))("my crazy default")(new Throwable("boom"))
f2: scala.concurrent.Future[String] = scala.concurrent.impl.Promise$DefaultPromise@1890516e

scala> f2 onComplete { println }
Success(my crazy default)

编辑:

为了完整起见,def craziness[A](future: Future[A]) 应该是 def craziness[A](future: => Future[A])

我认为您在 recoverrecoverWith 构造之后。这是一个快速的 REPL 会话来显示它的用法。

$ scala
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_45).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.concurrent.Future
import scala.concurrent.Future

scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global

scala> val failFuture = Future(sys.error("BOOM"))
failFuture: scala.concurrent.Future[Nothing] = scala.concurrent.impl.Promise$DefaultPromise@6e06451e

scala> val defaultValue = 100
defaultValue: Int = 100

scala> val futureRecoveredWithDefaultFuture = failFuture.recoverWith { case e: RuntimeException => Future.successful(defaultValue) }
futureRecoveredWithDefaultFuture: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@130161f7

scala> val futureRecoveredWithDefaultValue = failFuture.recover { case e: RuntimeException => defaultValue }
futureRecoveredWithDefaultValue: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@3b69e7d1

检查这是否真的有效:

scala> import scala.concurrent.duration._
import scala.concurrent.duration._

scala> import scala.concurrent.Await
import scala.concurrent.Await

scala> val res1 = Await.result(futureRecoveredWithDefaultFuture, 1.second)
res1: Int = 100

scala> val res2 = Await.result(futureRecoveredWithDefaultValue, 1.second)
res2: Int = 100

在 Scala 2.12 中,您将能够使用 transform and transformWith 使这变得微不足道。

但在那之前,这应该会让你到达那里:

implicit class InvertFuture[T](val fut: Future[T]) extends AnyVal {
  def flip(recover: Throwable => T)(fail: T => Throwable)(implicit ec: ExecutionContext): Future[T] =
        fut.recover({ case t => recover(t) }).map(t => throw fail(t))
}

// And usage:

scala> Future(1).flip(_ => 2)(_ => throw new IllegalStateException("ohnoes!")) onComplete println
Failure(java.lang.IllegalStateException: ohnoes!)