scala - 用 Future 满足抽象特征要求

标签 scala akka future

我有一个抽象特征,对计算有一些困难的要求,然后对这些计算的结果有一些函数。我想让这个特性保持简单,以便于理解和测试。

trait Calculator {
  def hardToCalculate1: Int
  def hardToCalculate2: Int
  def hardToCalculate3: Int

  def result1 = hardToCalculate1 + hardToCalculate2
  def result2 = hardToCalculate2 + hardToCalculate3
  def result3 = hardToCalculate1 + hardToCalculate3
}

当我实例化一个计算器时,我将使用Futures来计算这些hardToCalculate值。假设它们看起来像这样:

def f1 = future {
    println("calculating 1")
    1
}
def f2 = future {
    println("calculating 2")
    2
}
def f3 = future {
    println("calculating 3")
    3
}

所以,我可以像这样构造一个 Future[Calculator]:

val myCalc = for {
  m1 <- f1
  m2 <- f2
  m3 <- f3
} yield new Calculator {
  lazy val hardToCalculate1 = m1
  lazy val hardToCalculate2 = m2
  lazy val hardToCalculate3 = m3
}

然后,我可能会像这样使用 myCalc:

myCalc onComplete {
  case Success(calc) => println("Result: " + calc.result1)
}

但是当我这样做时,我得到了这个:

calculating 1
calculating 2
calculating 3
Result: 3

我只想在我正在进行的计算确实需要这些 future 时才执行它们。尽管我使用 lazy val 声明了 hardToCalculate,但所有三个都是在执行 Future[Calculator].onComplete 时计算的。

实现此目的的一种方法是这样的:

val calculator = new Calculator {
    lazy val hardToCalculate1 = Await.result(f1, 10 seconds)
    lazy val hardToCalculate2 = Await.result(f2, 10 seconds)
    lazy val hardToCalculate3 = Await.result(f3, 10 seconds)
}
println("result: " + calculator.result1)

这会产生我想要的结果:

calculating 1
calculating 2
result: 3

但现在我所有的 Await 都被阻塞了。我真正想要的是一个以惰性方式执行 future 的 Future[Calculator] 。如果不将 Futures 引入我的 Calculator 特征中,这可能吗?关于如何获得我想要的东西还有其他建议吗?

( A gist with all of the above code is here. )

最佳答案

如果您创建一个 Future(使用 scala.concurrent.future),无论您做什么,它都会被计算。所以你需要一个完全不同的策略。

此外,您的界面甚至不允许远程确定您将实际使用哪些数据。 myCalc 的计算如何知道在 onComplete 中您将只使用 result1

你可以:

  • 仅使用惰性值:

    val calculator = new Calculator {
      lazy val hardToCalculate1 = {
        println("calculating 1")
        1
      }
      // ...
    }
    

    优点:简单
    缺点:非异步

  • 封装Future以允许请求计算:

    class ReqFuture[T](body: () => T) {
      lazy val fut = future { body() }
    }
    

    但是现在您仍然遇到 myCalc 将请求并等待所有这些的问题。因此,您必须在 Calculator 中引入 ReqFutures:

    trait Calculator {
        def hardToCalculate1: ReqFuture[Int]
        // ...
        def result1 = for {
          h1 <- hardToCalculate1.fut
          h2 <- hardToCalculate2.fut
        } yield h1 + h2
    }
    

    优点:当您调用 result1 时,只会计算您需要的内容(但仍然只计算一次)。
    缺点:result1 现在是一个 Future[Int]。因此, future 已经完全渗透到您的计算器中。

如果您无法影响Calculator(我怀疑)并且无法更改result1,2,3的代码,那么不幸的是,据我所知,您无能为力使执行变得惰性和异步。

关于scala - 用 Future 满足抽象特征要求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17664359/

相关文章:

scala - 如何发现未追踪的 future ?

scala - 如何通过 Casbah 将带有列表的 Scala 对象转换为 MongoDBObject

java - 如何在 Java Play Framework 中在一周中的特定一天运行 akka 调度程序?

java - 无法加载 [akka.event.Logging$Error$]

java - 在 Java 中将监听器变成 future

scala - 解析 Seq[Future[Either[A, Seq[B]]]] - Scala Cats

java - 如何在rJava中表示NA

scala - 配置 scaladoc 以包含扩展方法

scala - Scala 中的磁盘持久化延迟缓存列表™

docker - ElasticBeanstalk中Docker中的Akka集群