假设我有一个函数fab: A => Future[B]
。现在,我需要编写新函数foo
来处理Seq[A]
并累积所有错误。这就是为什么我不能使用Future.traverse的原因,因为它“快速失败”并且不会累积错误。
foo
收到Seq[A]
,并应返回Future
。客户端应该为输入B
的每个元素获取Seq[A]
或一个异常。该函数的签名是什么?
答案 0 :(得分:1)
要根据需要定义foo
,请在将Future.sequence
应用于输入列表的各个元素之后,考虑在map/recover
上方使用fab
,如下所示:< / p>
import scala.concurrent.{ Future, ExecutionContext }
def foo[A, B](ls: List[A])(fab: A => Future[B])(implicit ec: ExecutionContext):
Future[List[Either[Throwable, B]]] =
Future.sequence(ls.map(fab).map(_.map(Right(_)).recover{ case e => Left(e) }))
请注意,首选使用不可变的Seq
而不是List
,因此这里使用它。如有必要,将其更改为Seq
。
测试foo
:
implicit val ec = ExecutionContext.global
def fab(s: String): Future[Int] = Future{ 10 / s.length }
val ls = List("abcd", "", "xx", "")
foo(ls)(fab)
// res1: Future[List[Either[Throwable, Int]]] = Future(Success(List(
// Right(2),
// Left(java.lang.ArithmeticException: / by zero),
// Right(5),
// Left(java.lang.ArithmeticException: / by zero)
// )))
答案 1 :(得分:0)
我有ZIO的解决方案。
我添加了这个伪造的函数:
def fab(implicit ec: ExecutionContext): Int => Future[String] = i => Future(
if (i % 3 == 0)
throw new IllegalArgumentException(s"bad $i")
else
s"$i"
)
现在,我们创建Int
的流,并为每个流运行fab
val stream =
ZStream.fromIterable(Seq(1, 2, 3, 4, 5))
.map(in => Task.fromFuture(implicit ec => fab(ec)(in)))
val sink = Sink.collectAll[Task[String]]
现在,我们收集成功和失败的信息:
val collect: ZIO[zio.ZEnv, Throwable, (List[String], List[Throwable])] = for {
strs <- stream.run(sink)
successes <- Task.collectAllSuccesses(strs)
failures <- ZIO.collectAllSuccesses(strs.map(_.flip))
} yield (successes, failures)
运行并打印此内容
new DefaultRuntime {}
.unsafeRun(
collect
.tapError { ex => zio.console.putStrLn(s"There was an exception: ${ex.getMessage}") }
.tap { case (successes, failures) => zio.console.putStrLn(s"($successes, $failures)") }
.fold(_ => -1, _ => 0)
)
打印我们:
(List(1, 2, 4, 5), List(java.lang.IllegalArgumentException: bad 3))
让我知道您是否需要更多说明-ZIO是否可以选择。