scala Seq [Futuro] para Futuro [Seq]

Tenho uma API que me dá Future[Int], já que tenho que operá-la em sequência de itens, minha resultante seria Seq[Future[Int]].

Quando exponho minha API, faz mais sentido ter em Future[Seq[Int]]vez de Seq[Future[Int]].

Mas o truque aqui é quando traduzo Seq [Futuro] para Futuro [Seq], o futuro tem dois estados

1) Falha
2) Sucesso

E se o Seq [Futuro] tiver um Future.Failure. Aqui está o exemplo

scala> val task : Seq[Future[Int]] = (1 to 3).map(x => if (x == 3) Future.failed(new RuntimeException("error")) else Future(x))
task
: Seq[scala.concurrent.Future[Int]] = Vector(Future(Success(1)), Future(Success(2)), Future(Failure(java.lang.RuntimeException: error)))

scala
> Future.sequence(task)
res6
: scala.concurrent.Future[Seq[Int]] = Future(<not completed>)

scala
> res6
res7
: scala.concurrent.Future[Seq[Int]] = Future(Failure(java.lang.RuntimeException: error))

O problema aqui é que Future.sequence é Future.Failure se encontrar uma única falha em uma sequência.

Solução

scala> import scala.util.Try
import scala.util.Try

scala
> import scala.util.Success
import scala.util.Success

scala
> import scala.util.Failure
import scala.util.Failure

scala
> val task: Seq[Future[Try[Int]]] = (1 to 3).map(x => if (x == 3) Future.successful(Failure(new RuntimeException("error"))) else Future(Success(x)))
task
: Seq[scala.concurrent.Future[scala.util.Try[Int]]] = Vector(Future(Success(Success(1))), Future(Success(Success(2))), Future(Success(Failure(java.lang.RuntimeException: error))))

scala
> Future.sequence(task)
res8
: scala.concurrent.Future[Seq[scala.util.Try[Int]]] = Future(<not completed>)

scala
> res8
res9
: scala.concurrent.Future[Seq[scala.util.Try[Int]]] = Future(Success(Vector(Success(1), Success(2), Failure(java.lang.RuntimeException: error))))

Além disso, posso filtrar as falhas agora,

scala> res8.map(_.filter(_.isSuccess))
res11
: scala.concurrent.Future[Seq[scala.util.Try[Int]]] = Future(<not completed>)

scala
> res11
res12
: scala.concurrent.Future[Seq[scala.util.Try[Int]]] = Future(Success(Vector(Success(1), Success(2))))

Agora, o resultado pode ser simplificado para Future[Seq],

scala> res8.map(_.filter(_.isSuccess).map(_.get))
res13
: scala.concurrent.Future[Seq[Int]] = Future(<not completed>)

scala
> res13
res14
: scala.concurrent.Future[Seq[Int]] = Future(Success(Vector(1, 2)))