Scala 将异步调用与 Future 同步

Scala Synchronising Asynchronous calls with Future

我有一个方法可以查找几个数据库并执行一些逻辑。

我return从方法中得到的MyType对象如下:

case class MyResultType(typeId: Long, type1: Seq[Type1], type2: Seq[Type2])

方法定义是这样的:

def myMethod(typeId: Long, timeInterval: Interval) = async {

  // 1. check if I can find an entity in the database for typeId  
  val myTypeOption = await(db.run(findMyTypeById(typeId))) // I'm getting the headOption on this result

  if (myTypeOption.isDefined) {

    val anotherDbLookUp = await(doSomeDBStuff) // Line A

    // the interval gets split and assume that I get a List of thse intervals
    val intervalList = splitInterval(interval)

    // for each of the interval in the intervalList, I do database look up
    val results: Seq[(Future[Seq[Type1], Future[Seq[Type2])] = for {
      interval <- intervalList
    } yield {
      (getType1Entries(interval), getType2Entries(interval))
    }
    // best way to work with the results so that I can return MyResultType 
  }
  else {
    None
  }
}

现在 getType1Entries(interval)getType2Entries(interval) 每个 return 都是 Seq(Type1)Seq(Type2) 条目的 Future

我现在的问题是从 Future 中取出 Seq(Type1)Seq(Type2) 并将其填充到 MyResultType 案例中 class?

你可以参考你问的这个问题

所以你得到了

val results2: Future[Seq([Iterable[Type1], [Iterable[Type2])] = ???

然后在其上调用 await 并且你根本没有 Futures,你可以做你想做的。

希望我理解正确。

哦,顺便说一句,你应该映射 myTypeOption 而不是检查它是否已定义并返回 None 如果没有

if (myTypeOption.isDefined) {
  Some(x)
} else {
  None
}

可以简单地替换为

myTypeOption.map { _ => // ignoring what actually was inside option
  x                     // return whatever you want, without wrapping it in Some
}

如果我正确理解了你的问题,那么这应该可以解决问题。

def myMethod(typeId: Long, timeInterval: Interval): Option[Seq[MyResultType]] = async {

    // 1. check if I can find an entity in the database for typeId
    val myTypeOption = await(db.run(findMyTypeById(typeId))) // I'm getting the headOption on this result

    if (myTypeOption.isDefined) {

      // the interval gets split and assume that I get a List of thse intervals
      val intervalList = splitInterval(interval)

      // for each of the interval in the intervalList, I do database look up
      val results: Seq[(Future[Seq[Type1]], Future[Seq[Type2]])] = for {
        interval <- intervalList
      } yield {
          (getType1Entries(interval), getType2Entries(interval))
      }
      // best way to work with the results so that I can return MyResultType
      Some(
        await(
          Future.sequence(
            results.map{
              case (l, r) =>
                l.zip(r).map{
                  case (vl, vr) => MyResultType(typeId, vl, vr)
                }
            })))
    }
    else {
      None
    }
  }

您的问题分为两个部分,1) 如何处理两个相关的 future,以及 2) 如何提取结果值。

在处理依赖期货时,我通常将它们组合在一起:

val future1 = Future { 10 }
val future2 = Future { 20 }

// results in a new future with type (Int, Int)
val combined = for {
  a <- future1
  b <- future2
} yield (a, b)

// then you can use foreach/map, Await, or onComplete to do
// something when your results are ready..
combined.foreach { ((a, b)) =>
  // do something with the result here
}

提取结果我一般需要做同步响应就用Await,如果需要处理潜在的失败就用_.onComplete(),用_.foreach()/_.map() 对于大多数其他情况。