可能失败的多个期货 - 返回成功和失败?
Multiple futures that may fail - returning both successes and failures?
我有一种情况需要运行并行操作。
所有操作都具有相同的 return 值(比如 Seq[String]
)。
有可能部分操作失败,而其他操作成功 return 结果。
我想 return 成功的结果和发生的任何异常,所以我可以记录它们以进行调试。
在我编写自己的 class 之前,是否有内置方法或通过任何库 (cats/scalaz) 的简单方法来执行此操作?
我想在自己的未来做每个操作,然后检查每个未来,然后 returning 一个 Seq[String] -> Seq[Throwable]
的元组,其中左值是成功的结果(展平/组合)和右边是发生的所有异常的列表。
有没有更好的方法?
听起来像是 Try
习语的一个很好的用例(它基本上类似于 Either
monad)。
来自 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)
}
它实际上比您要求的要多一点,因为它是完全异步的。它适合您的用例吗?
正在给您的 Future
returns 和 Option[Try[T]]
打电话 value
。如果 Future
尚未完成,则 Option
为 None
。如果它已经完成,那么它很容易解包和处理。
if (myFutr.isCompleted)
myFutr.value.map(_.fold( err: Throwable => //log the error
, ss: Seq[String] => //process results
))
else
// do something else, come back later
使用您在评论中提到的 Await.ready
,通常会失去使用期货的大部分好处。相反,您可以使用普通的 Future
组合器来做到这一点。让我们做一个更通用的版本,它适用于任何 return 类型;可以轻松添加扁平化 Seq[String]
s。
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.
类型可以由编译器推断,显示它们只是为了帮助您了解逻辑。
我会这样做:
import scala.concurrent.{Future, ExecutionContext}
import scala.util.Success
def eitherify[A](f: Future[A])(implicit ec: ExecutionContext): Future[Either[Throwable, A]] = f.transform(tryResult => Success(tryResult.toEither))
def eitherifyF[A, B](f: A => Future[B])(implicit ec: ExecutionContext): A => Future[Either[Throwable, B]] = { a => eitherify(f(a)) }
// here we need some "cats" magic for `traverse` and `separate`
// instead of `traverse` you can use standard `Future.sequence`
// there is no analogue for `separate` in the standard library
import cats.implicits._
def myProgram[A, B](values: List[A], asyncF: A => Future[B])(implicit ec: ExecutionContext): Future[(List[Throwable], List[B])] = {
val appliedTransformations: Future[List[Either[Throwable, B]]] = values.traverse(eitherifyF(asyncF))
appliedTransformations.map(_.separate)
}
我有一种情况需要运行并行操作。
所有操作都具有相同的 return 值(比如 Seq[String]
)。
有可能部分操作失败,而其他操作成功 return 结果。
我想 return 成功的结果和发生的任何异常,所以我可以记录它们以进行调试。
在我编写自己的 class 之前,是否有内置方法或通过任何库 (cats/scalaz) 的简单方法来执行此操作?
我想在自己的未来做每个操作,然后检查每个未来,然后 returning 一个 Seq[String] -> Seq[Throwable]
的元组,其中左值是成功的结果(展平/组合)和右边是发生的所有异常的列表。
有没有更好的方法?
听起来像是 Try
习语的一个很好的用例(它基本上类似于 Either
monad)。
来自 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)
}
它实际上比您要求的要多一点,因为它是完全异步的。它适合您的用例吗?
正在给您的 Future
returns 和 Option[Try[T]]
打电话 value
。如果 Future
尚未完成,则 Option
为 None
。如果它已经完成,那么它很容易解包和处理。
if (myFutr.isCompleted)
myFutr.value.map(_.fold( err: Throwable => //log the error
, ss: Seq[String] => //process results
))
else
// do something else, come back later
使用您在评论中提到的 Await.ready
,通常会失去使用期货的大部分好处。相反,您可以使用普通的 Future
组合器来做到这一点。让我们做一个更通用的版本,它适用于任何 return 类型;可以轻松添加扁平化 Seq[String]
s。
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.
类型可以由编译器推断,显示它们只是为了帮助您了解逻辑。
我会这样做:
import scala.concurrent.{Future, ExecutionContext}
import scala.util.Success
def eitherify[A](f: Future[A])(implicit ec: ExecutionContext): Future[Either[Throwable, A]] = f.transform(tryResult => Success(tryResult.toEither))
def eitherifyF[A, B](f: A => Future[B])(implicit ec: ExecutionContext): A => Future[Either[Throwable, B]] = { a => eitherify(f(a)) }
// here we need some "cats" magic for `traverse` and `separate`
// instead of `traverse` you can use standard `Future.sequence`
// there is no analogue for `separate` in the standard library
import cats.implicits._
def myProgram[A, B](values: List[A], asyncF: A => Future[B])(implicit ec: ExecutionContext): Future[(List[Throwable], List[B])] = {
val appliedTransformations: Future[List[Either[Throwable, B]]] = values.traverse(eitherifyF(asyncF))
appliedTransformations.map(_.separate)
}