j3d
j3d

Reputation: 9734

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

Upvotes: 3

Views: 5044

Answers (2)

Gabriele Petronella
Gabriele Petronella

Reputation: 108169

recoverWith should return a Future of something, so your example won't even compile.

You can use recover instead

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.recover { case _ => None }
})

Also, in your specific example you are not even using r2, so you can do just

val results = Future.sequence(l.map { i =>
  f1(i).map(Some(_)).recover { case _ => None }
})

Upvotes: 3

Shyamendra Solanki
Shyamendra Solanki

Reputation: 8851

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

Upvotes: 5

Related Questions