Scala For-Comprehension: как восстановить и продолжить, если будущее не удастся

Учитывая следующие List целых чисел...

val l = List(1, 2, 3)

... Мне нужно вызвать 2 метода, которые возвращают Future для каждого элемента и получают следующий результат:

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

Вот моя попытка:

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

Если f2 выйдет из строя, я хочу восстановить и продолжить работу с оставшимися элементами. Приведенный выше код не работает, так как recoverWith никогда не вызывается, даже если f2 терпит неудачу.

Как восстановиться после сбоя f2, чтобы окончательный результат был примерно таким?

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

Второй элемент должен быть None, потому что f2 не работает, когда входное целое число четное (т.е. 2).


person j3d    schedule 04.10.2015    source источник


Ответы (2)


когда recoverWith имеет тип вывода как Future, он работает нормально.

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 

результат:

// Called recover 2
// Success(List(Some(1), None, Some(3))       

// tried with scala version: 2.10.4
person Shyamendra Solanki    schedule 04.10.2015

recoverWith должно возвращать Future чего-то, поэтому ваш пример даже не скомпилируется.

Вместо этого вы можете использовать recover

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

Кроме того, в вашем конкретном примере вы даже не используете r2, поэтому вы можете просто

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