scala如何等待几个期货

时间:2016-08-04 14:33:17

标签: scala future

我有seq

我想自动进行迭代

3 个答案:

答案 0 :(得分:1)

您可以将iteratorflatMap一起使用。这是一个简化的例子:

import scala.concurrent.Await
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

val functions: Seq[() => Future[Int]] = Seq(
  () => Future(1),
  () => Future(2),
  () => Future(3),
  () => Future(4),
  () => Future(5)
)

def checkResult(result: Int): Boolean = result > 3

functions.iterator.flatMap { func =>
  val result = Await.result(func(), 5.seconds)
  if (checkResult(result)) Some(result) else None
}.next()

此代码将返回 4

更新:

如果您只关心正确的结果并且不关心所有期货是否都已执行,您可以使用fallBackTo链接它们:

import scala.util._

functions.tail.foldLeft[Future[Int]](functions.head().filter(checkResult)) {
    case (result, function) =>
      result.fallbackTo(function().filter(checkResult))
}.onComplete {
  case Success(result) => s"Got one: $result"
  case Failure(exception) => s"Damn! Got NO result due to: $exception"
}

通过我的示例谓词Future,这将为Success(4)提供x > 3

更新2

当我发现自己有一些额外的时间时,我冒昧地将其他答案(特别是来自Alexander Azarov)的一些评论和建议与贷款模式方法结合起来:

import scala.annotation.tailrec
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

val functions: Seq[() => Future[Int]] = Seq(
  () => Future(1),
  () => Future(2),
  () => Future(3),
  () => Future(4),
  () => Future(5)
)

def checkResult(result: Int): Boolean = result > 5

def withFirstValidResult[A, B](futureFuncs: Seq[() => Future[A]], predicate: A => Boolean, defaultValue: A)(op: (A) => B): Future[B] = {
  @tailrec
  @inline def find(remaining: Iterator[Future[A]]): Future[A] = {
    if (remaining.hasNext) {
      remaining.next().filter(predicate).recoverWith {
        case _ =>
        find(remaining)
      }
    } else {
      Future.successful {
        println(s"No valid result found, falling back to default: $defaultValue")
        defaultValue
      }
    }
  }


  find(futureFuncs.iterator.map(_())).map(op)
}

您现在可以将此功能用于您想要的任何期货,并提供应该使用第一个有效结果或defaultValue执行的操作(尽管如果没有有效结果,恕我直言,我更喜欢{{{ 1}}我自己正确处理错误):

Failure

作为额外的提示,如果你想加速这个功能,你可以让迭代器懒惰地用withFirstValidResult(functions.iterator, checkResult, 0) { result => println(s"Got a valid result: $result") }.onFailure { case ex => println(s"Got NO valid result: $ex") } 一次评估两个函数。这样,在评估未来的一个结果时,迭代器将自动开始下一个未来。

答案 1 :(得分:1)

def firstF[A](seq: Seq[() => Future[A]], predicate: A => Boolean): Future[A] =
  seq.head().filter(predicate).recoverWith {
    case _ => firstF[A](seq.tail, predicate)
  }

方法firstF将返回与指定条件匹配的第一个Future。如果输入Future没有匹配,则生成的Failure将为Future。要返回一些默认值,您只需执行

firstF(seq, predicate).recover { case _ => default }

注1 此处的代码演示了基本原则,并没有区分计划内或计划外的异常。

注意2 此代码不是尾递归的。

答案 2 :(得分:0)

import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

def f(x:String) = Future { x }

val functions = Seq(
  () => f("a"),
  () => f("b"),
  () => f(""),
  () => f("d"),
  () => f("e"))

functions.takeWhile( x => Await.result(x(), 5 seconds) != "")

这会给你

res0: Seq[() => scala.concurrent.Future[String]] = List(<function0>, <function0>)

我不确定是否有其他方法,但根据我的理解,如果你想根据前面的未来结果做出决定,你需要等待。

修改 这是一种递归方法。但在我看来,使用迭代器的那个很好。

import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

def f(x:String) = Future { x }

val functions = Seq(
  () => f("a"),
  () => f("b"),
  () => f(""),
  () => f("d"),
  () => f("e"))


check(functions, 0)
def check(list:Seq[() => Future[String]], pos:Int):String = {
  if(list.size <= pos) return ""

  val res = Await.result(list(pos)(), 5 seconds)
  if(condition(res)) {
    res
  }
  else check(list, pos+1)
}

def condition(c:String):Boolean = if(c == "d") true else false

它的尾部是递归的,因此你的堆栈不会轻易溢出。