5

我一直在尝试简化我在 Scala 中做期货的方式。我得到了一点 aFuture[Option[Future[Option[Boolean]]但我在下面进一步简化了它。有没有更好的方法来简化这个?

通过“失败”的未来似乎并不是最好的方法。即在顺序世界中,我只是返回“失败!!” 任何时候它都失败了,而不是继续到最后。还有其他方法吗?

val doSimpleWork = Future {
  //Do any arbitrary work (can be a different function)
  true //or false
}

val doComplexWork = Future {
  //Do any arbitrary work (can be a different function)
  Some("result") //or false
}

val failed = Future {
  //Do no work at all!!! Just return
  false
}

val fut1 = doSimpleWork
val fut2 = doSimpleWork

val fut3 = (fut1 zip fut2).map({
  case (true, true) => true
  case _ => false
})

val fut4 = fut3.flatMap({
  case true =>
    doComplexWork.flatMap({
      case Some("result") =>
        doSimpleWork
      case None =>
        failed
    })
  case false =>
    failed
})

fut4.map({
  case true =>
    "SUCCESS!!!"
  case _ =>
    "FAIL!!"
})
4

3 回答 3

3

请注意,在您的示例中,因为您急切地Futures将 a 实例化为 a val,所以一旦您声明它们 ( val x = Future {...}),它们都将开始执行。相反,使用方法将使 Futures 仅在执行链请求它们时执行。

避免进一步计算的一种方法是抛出异常,然后使用onFailure

def one = future { println("one") ; Some(1) }
def two = future { println("two") ; throw new Exception("no!"); 2 }
def three = future { println("three") ; 3 }

val f = one flatMap {
  result1 => two flatMap {
    result2 => three
  }
}

f onFailure {
  case e: Exception =>
    println("failed somewhere in the chain")
}

你可以在这里看到“三”不应该被打印出来,因为我们在two. 情况是这样的:

one 
two 
failed somewhere in the chain
于 2013-04-30T06:27:32.630 回答
3

“Monad 转换器”是一种结构,可让您组合两个 monad 的“效果”,scalaz 项目提供了几种不同的 monad 转换器。我的建议是,如果您还利用与布尔( ==和== )Option[Unit]同构的事实,您可以使用 OptionT monad 转换器来简化您的代码。这是一个完整的例子:Some(())trueNonefalse

import scalaz._
import Scalaz._
import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.concurrent.duration._
object Foo {

  // We need a Monad instance for Future, here is a valid one, or you can use the implementation
  // in the scalaz-contrib project, see http://typelevel.org
  implicit def futureMonad(implicit executor: ExecutionContext): Monad[Future] = new Monad[Future]  {
    override def bind[A, B](fa: Future[A])(f: A ⇒ Future[B]) = fa flatMap f
    override def point[A](a: ⇒ A) = Future(a)
    override def map[A, B](fa: Future[A])(f: A ⇒ B) = fa map f
  }

  // OptionT allows you to combine the effects of the Future and Option monads
  // to more easily work with a Future[Option[A]]
  val doSimpleWork : OptionT[Future,Unit] = OptionT(Future {
    // Option[Unit] is isomorphic to Boolean
    Some(()) //or None
  })

  val simpleFail : OptionT[Future,Unit] = OptionT(Future {
    None
  })

  val doComplexWork: OptionT[Future,String] = OptionT(Future {
    Some("result") //or None
  })

  val f1 = doSimpleWork
  val f2 = doSimpleWork
  val f3 = doComplexWork
  val f4 = doSimpleWork

  def main(argv: Array[String]) {
    val result = for {
      _ <- f1
      // we don't get here unless both the future succeeded and the result was Some
      _ <- f2  
      _ <- f3
      r <- f4
    } yield(r)

    result.fold((_ => println("SUCCESS!!")),println("FAIL!!"))

    // "run" will get you to the Future inside the OptionT
    Await.result(result.run, 1 second)
  }
}
于 2013-04-30T13:41:58.763 回答
1

你可以尝试这样的事情,使用 for comprehensions 来清理代码:

  def doSimpleWork = Future{
    //do some simple work
    true
  }

  def doComplexWork = Future{
    //do something complex here
    Some("result")
  }

  val fut1 = doSimpleWork
  val fut2 = doSimpleWork

  val fut = for{
    f1Result <- fut1
    f2Result <- fut2
    if (f1Result && f2Result)
    f3Result <- doComplexWork
    if (f3Result.isDefined)
    f4Result <- doSimpleWork
  } yield "success"

  fut onComplete{
    case Success(value) => println("I succeeded")
    case Failure(ex) => println("I failed: " + ex.getMessage)
  }

如果您真的只想在最后打印出“成功”或“失败”,您可以将最后一段代码更改为:

  fut.recover{case ex => "failed"} onSuccess{
    case value => println(value)
  }

现在,解释发生了什么。对于初学者,我们定义了两个返回的函数,Futures它们正在做一些异步工作。doSimpleWork 函数将做一些简单的工作并返回一个布尔成功/失败指示器。doComplexWork 函数将做一些更复杂(且耗时)的事情,并返回一个表示结果的 Option[String]。然后,在进入 for 理解之前,我们启动了 doSimpleWork 的两个并行调用。在 for for comp 中,我们在检查它们是否都成功之前获得fut1and的结果(按此顺序)。fut2如果不是,我们将在此停止,futval 将失败并显示NoSuchElementException当这样的条件在 for comp 中失败时,这就是你得到的。如果两者都成功,我们将继续并调用 doComplexWork 函数并等待其结果。然后我们将检查它的结果,如果是Some,我们将启动最后一次 doSimpleWork 调用。如果成功,我们将产生字符串“success”。如果您检查futval 的类型,则它的类型为Future[String]

从那里,我们使用其中一个异步回调函数来检查整个调用序列是否一直通过(Success案例),或者在过程中的某个地方失败(Failure案例),打印出与哪个案例相关的内容打。在备用的最终代码块中,我们通过返回字符串“失败”“从任何可能的失败中恢复,然后仅使用回调,该onSuccess回调将根据发生的情况打印“成功”或“失败”。

于 2013-04-30T12:01:49.837 回答