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)))