可能会失败的多个期货-成功与失败并存?

时间:2018-09-22 09:17:08

标签: scala concurrency parallel-processing scalaz scala-cats

我遇到需要并行运行一堆操作的情况。

所有操作都具有相同的返回值(例如Seq[String])。

某些操作可能会失败,而其他操作可能会成功返回结果。

我想同时返回成功的结果和发生的任何异常,因此我可以将它们记录下来进行调试。

在我编写自己的类来执行此操作之前,是否有内置方法或通过任何库(cats / scalaz)进行访问的简便方法?

我正在考虑在自己的将来进行每个操作,然后检查每个将来,并返回一个Seq[String] -> Seq[Throwable]元组,其中左边的值是成功的结果(展平/合并),右边的是任何异常的列表发生了。

有更好的方法吗?

4 个答案:

答案 0 :(得分:2)

使用Await.ready(您在评论中提到)通常会失去使用期货的大部分好处。相反,您可以仅使用常规的Future组合器来执行此操作。让我们做一个更通用的版本,它适用于任何返回类型。扁平化Seq[String]可以轻松添加。

def successesAndFailures[T](futures: Seq[Future[T]]): Future[(Seq[T], Seq[Throwable])] = {
  // first, promote all futures to Either without failures
  val eitherFutures: Seq[Future[Either[Throwable, T]]] = 
    futures.map(_.transform(x => Success(x.toEither)))
  // then sequence to flip Future and Seq
  val futureEithers: Future[Seq[Either[Throwable, T]]] = 
    Future.sequence(eitherFutures)
  // finally, Seq of Eithers can be separated into Seqs of Lefts and Rights
  futureEithers.map { seqOfEithers =>
    val (lefts, rights) = seqOfEithers.partition(_.isLeft)
    val failures = lefts.map(_.left.get)
    val successes = rights.map(_.right.get)
    (successes, failures)
  }
}

Scalaz and Cats have separate to simplify the last step.

编译器可以推断出类型,它们的显示只是为了帮助您了解逻辑。

答案 1 :(得分:1)

听起来像Try惯用法的好用例(它基本上与Either单子相似)。

doc中的用法示例:

import scala.util.{Success, Failure}

val f: Future[List[String]] = Future {
  session.getRecentPosts
}

f onComplete {
  case Success(posts) => for (post <- posts) println(post)
  case Failure(t) => println("An error has occurred: " + t.getMessage)
}

它实际上比您要求的功能多一点,因为它是完全异步的。是否适合您的用例?

答案 2 :(得分:1)

value上调用Future会返回Option[Try[T]]。如果Future尚未完成,则OptionNone。如果已完成,则很容易解包和处理。

if (myFutr.isCompleted)
  myFutr.value.map(_.fold( err: Throwable  => //log the error
                         , ss: Seq[String] => //process results
                         ))
else
 // do something else, come back later

答案 3 :(得分:0)

我会这样:

<id>machine</id>
<modules>
    <module>../test1</module>
    <module>../test2</module>
    <module>../test3</module>
</modules>