Search code examples
scalafuturefor-comprehension

Scala For-Comprehension: How to Recover and Continue If a Future Fails


Given the following List of integers...

val l = List(1, 2, 3)

... I need to invoke 2 methods that return a Future on each element and obtain the following result:

Future(Some(1), Some(2), Some(3))

Here below is my try:

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def f1(i: Int) = Future(i)
def f2(i: Int) = Future { if (i % 2 == 0) throw new Exception else i }

val l = List(1, 2, 3)

val results = Future.sequence(l.map { i =
  val f = for {
    r1 <- f1(i)
    r2 <- f2(i) // this throws an exception if i is even
  } yield Some(r1)

  f.recoverWith {
    case e => None
  }
})

If f2 fails, I want to recover and continue with the remaining elements. The code above doesn't work since recoverWith is never invoked, even if f2 fails.

How do I recover when f2 fails so that the final result is something like this?

Future(Some(1), None, Some(3))

The second element should be None because f2 fails when the input integer is even (i.e. 2).


Solution

  • when recoverWith has output type as Future, it works fine.

    import scala.concurrent.Future
    import scala.concurrent.ExecutionContext.Implicits.global
    
    def f1(i: Int) = Future(i)
    def f2(i: Int) = Future { if (i % 2 == 0) throw new Exception else i }
    
    val l = List(1, 2, 3)
    
    val results = Future.sequence(l.map { i =>
      val f = for {
        r1 <- f1(i)
        r2 <- f2(i) // this might throw an exception
      } yield Some(r1)
    
      f.recoverWith {
        case e => Future { println("Called recover " + i); None } // wrapped in Future
      }
    })
    results onComplete println 
    

    result:

    // Called recover 2
    // Success(List(Some(1), None, Some(3))       
    
    // tried with scala version: 2.10.4