I'm looking for a way to convert an arbitrary length list of Futures to a Future of List. I'm using Playframework, so ultimately, what I really want is a Future[Result]
, but to make things simpler, let's just say Future[List[Int]]
The normal way to do this would be to use Future.sequence(...)
but there's a twist... The list I'm given usually has around 10-20 futures in it, and it's not uncommon for one of those futures to fail (they are making external web service requests). Instead of having to retry all of them in the event that one of them fails, I'd like to be able to get at the ones that succeeded and return those.
For example, doing the following doesn't work
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success
import scala.util.Failure
val listOfFutures = Future.successful(1) :: Future.failed(new Exception("Failure")) ::
Future.successful(3) :: Nil
val futureOfList = Future.sequence(listOfFutures)
futureOfList onComplete {
case Success(x) => println("Success!!! " + x)
case Failure(ex) => println("Failed !!! " + ex)
}
scala> Failed !!! java.lang.Exception: Failure
Instead of getting the only the exception, I'd like to be able to pull the 1 and 3 out of there. I tried using Future.fold
, but that apparently just calls Future.sequence
behind the scenes.
Thanks in advance for the help!
Scala 2.12 has an improvement on
Future.transform
that lends itself to the anwser with less codes.I tried Kevin's answer, and I ran into a glitch on my version of Scala (2.11.5)... I corrected that, and wrote a few additional tests if anyone is interested... here is my version >
I just came across this question and have another solution to offer:
The idea here is that within the fold you are waiting for the next element in the list to complete (using the for-comprehension syntax) and if the next one fails you just fallback to what you already have.
The trick is to first make sure that none of the futures has failed.
.recover
is your friend here, you can combine it withmap
to convert all theFuture[T]
results toFuture[Try[T]]]
instances, all of which are certain to be successful futures.note: You can use
Option
orEither
as well here, butTry
is the cleanest way if you specifically want to trap exceptionsThen use
Future.sequence
as before, to give you aFuture[List[Try[T]]]
Then filter:
You can even pull out the specific failures, if you need them:
You can easily wraps future result with option and then flatten the list: