给定以下方法...

def doSomething1: Future[Int] = { ... }
def doSomething2: Future[Int] = { ... }
def doSomething3: Future[Int] = { ... }


...以及以下理解:

for {
  x <- doSomething1
  y <- doSomething2
  z <- doSomething3
} yield x + y + z


这三种方法并行运行,但是在我的情况下,doSomething2必须在doSomething1完成后运行。如何依次运行这三种方法?

编辑

正如Philosophus42所建议的,以下是doSomething1的可能实现:

def doSomething1: Future[Int] = {
  // query the database for customers younger than 40;
  // `find` returns a `Future` containing the number of matches
  customerService.find(Json.obj("age" -> Json.obj("$lt" -> 40)))
}


...因此Future是通过内部调用另一个方法创建的。

编辑2

也许我简化了用例……很抱歉。让我们再试一次,更接近实际用例。这是三种方法:

for {
  // get all the transactions generated by the exchange service
  transactions <- exchange.orderTransactions(orderId)

  //for each transaction create a log
  logs <- Future.sequence(tansactions.map { transaction =>
    for {
      // update trading order status
      _ <- orderService.findAndUpdate(transaction.orderId, "Executed")

      // create new log
      log <- logService.insert(Log(
        transactionId => transaction.id,
        orderId => transaction.orderId,
        ...
      ))
    } yield log
  })
} yield logs


我想做的是为与订单关联的每个交易创建一个日志。即使logService.insert仅包含一个条目,也会多次调用transactions

最佳答案

评论你的帖子

首先,doSomethingX中的代码是什么样的?更恼怒的是,使用给定的代码,期货将并行运行。

回答

为了使Future执行顺序,只需使用

for {
  v1 <- Future { ..block1... }
  v2 <- Future { ..block2... }
} yield combine(v1, v2)


之所以起作用,是因为语句Future {..body ..}在该时间点开始异步计算。

有了以上的领悟

Future { ..block1.. }
  .flatMap( v1 =>
     Future { ..block>.. }
       .map( v2 => combine(v1,v2) )
  )


很明显,


如果Future{ ...block1... }有可用的结果,
flatMap方法被触发,
然后触发Future { ...block2... }的执行。


因此,在Future { ...block2... }之后执行Future { ...block1... }

附加信息

Future

Future {
  <block>
}


立即通过ExecutionContext触发包含的块的执行

片段1:

val f1 = Future { <body> }
val f2 = Future { <otherbody> }


这两个计算正在并行运行(以这种方式设置ExecutionContext的情况),因为这两个值会立即求值。

片段2:

构造

def f1 = Future { ..... }


一旦调用f1,将开始执行未来

编辑:

j3d,我仍然感到困惑,为什么您的代码无法按预期运行,如果您的陈述正确,那么Future是在computeSomethingX方法中创建的。

这是证明computeSomething2computeSomething1之后执行的代码段

导入scala.concurrent。{等待,未来}
导入scala.concurrent.duration._

object Playground {

  import scala.concurrent.ExecutionContext.Implicits.global

  def computeSomething1 : Future[Int] = {
    Future {
      for (i <- 1 to 10) {
        println("computeSomething1")
        Thread.sleep(500)
      }
      10
    }
  }

  def computeSomething2 : Future[String] = {
    Future {
      for(i <- 1 to 10) {
        println("computeSomething2")
        Thread.sleep(800)
      }
      "hello"
    }
  }

  def main(args: Array[String]) : Unit = {

    val resultFuture: Future[String] = for {
      v1 <- computeSomething1
      v2 <- computeSomething2
    } yield v2 + v1.toString

    // evil "wait" for result

    val result = Await.result(resultFuture, Duration.Inf)

    println( s"Result: ${result}")
  }
}


带输出

computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething1
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
computeSomething2
Result: hello10


编辑2

如果希望它们并行执行,请预先创建期货(此处为f1f2

def main(args: Array[String]) : Unit = {
  val f1 = computeSomething1
  val f2 = computeSomething2

  val resultFuture: Future[String] = for {
    v1 <- f1
    v2 <- f2
  } yield v2 + v1.toString

  // evil "wait" for result

  val result = Await.result(resultFuture, Duration.Inf)

  println( s"Result: ${result}")
}

07-24 19:05
查看更多