有条件的期货链

时间:2016-06-08 19:36:22

标签: scala

我有一系列参数。对于每个参数,我必须执行数据库查询,这可能会也可能不会返回结果。简单来说,我需要在第一个结果非空后停止。当然,我想避免做不必要的电话。需要注意的是 - 我需要将此操作作为另一个未来 - 或任何“最具反应性”的方法。 说到代码:

for each emp no-lock by emp.FirstName desc by emp.LastName :

我知道我可以将整个函数包装在另一个//that what I have def dbQuery(p:Param): Future[Option[Result]] = {} //my list of params val input = Seq(p1,p2,p3) //that what I need to implements def getFirstNonEmpty(params:Seq[Param]): Future[Option[Result]] 中并按顺序执行代码(Await?Brrr ...),但这不是最干净的解决方案。 我可以以某种方式创建懒惰的初始化期货集合,如

Future

我相信这是可能的!

3 个答案:

答案 0 :(得分:1)

你怎么看待这样的事情?

def getFirstNonEmpty(params: Seq[Param]): Future[Option[Result]] = {
  params.foldLeft(Future.successful(Option.empty[Result])) { (accuFtrOpt, param) =>
    accuFtrOpt.flatMap {
      case None   => dbQuery(param)
      case result => Future.successful(result)
    }
  }
}

答案 1 :(得分:0)

这可能有点矫枉过正,但如果您愿意使用scalaz,我们可以使用OptionTfoldMap执行此操作。

使用OptionT,我们将FutureOption组合成一个结构。我们可以使用Future获取带有非空结果的两个OptionT.orElse中的第一个。

import scalaz._, Scalaz._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

val someF: Future[Option[Int]] = Future.successful(Some(1))
val noneF: Future[Option[Int]] = Future.successful(None)

val first = OptionT(noneF) orElse OptionT(someF)
first.run // Future[Option[Int]] = Success(Some(1))

我们现在可以从标准库中Future获取List的第一个非空reduce(但这将运行所有Future s):< / p>

List(noneF, noneF, someF).map(OptionT.apply).reduce(_ orElse _).run 

但是对于List(或其他集合),我们无法确定至少有一个元素,因此我们需要使用fold并传递起始值。 Scalaz可以使用Monoid为我们完成这项工作。我们将使用的Monoid[OptionT[Future, Int]]将提供起始值,并将Future与上面使用的orElse合并。

type Param = Int
type Result = Int
type FutureO[x] = OptionT[Future, x]

def query(p: Param): Future[Option[Result]] = 
  Future.successful{ println(p); if (p > 2) Some(p) else None }

def getFirstNonEmpty(params: List[Param]): Future[Option[Result]] = {
  implicit val monoid = PlusEmpty[FutureO].monoid[Result]
  params.foldMap(p => OptionT(query(p))).run
}

val result = getFirstNonEmpty(List(1,2,3,4))
// prints 1, 2, 3
result.foreach(println) // Some(3)

答案 2 :(得分:0)

这是一个古老的问题,但是如果有人来寻找答案,这是我的看法。我针对一个用例解决了该问题,该用例要求我依次循环访问有限数量的期货,并在第一个期货返回结果时停止。

我的用例不需要一个库,轻量级的递归和模式匹配就足够了。尽管这里的问题与期货序列没有相同的问题,但是遍历一系列参数将是相似的。

这是基于递归的伪代码。

我尚未编译此文件,请修复要匹配/返回的类型。

def getFirstNonEmpty(params: Seq[Param]): Future[Option[Result]] = {
    if (params.isEmpty) {
      Future.successful(None)
    } else {
      val head = params.head
      dbQuery(head) match {           
         case Some(v) => Future.successful(Some(v)) 
         case None => getFirstNonEmpty(params.tail)          
      }
    }
}