如何转换 Scala 中失败的未来异常?

How to transform failed future exceptions in Scala?

我一直使用recover来转换类似于

的失败期货中的异常
def selectFromDatabase(id: Long): Future[Entity] = ???

val entity = selectFromDatabase(id) recover {   
  case e: DatabaseException =>
    logger.error("Failed ...", e)
    throw new BusinessException("Failed ...", e) 
}

此代码片段将 DatabaseException 转换为 BusinessException。但是,根据问题的评论:

... generally speaking the point of "recover" and "recoverWith" is not to simply transform your exceptions from one type to another, but to recover from the failure by performing the task in a different way so that you no longer have a failure.

所以显然我不应该使用 recover 来转换异常。转换 Future 异常/失败 Future 的正确方法是什么?

既然你只是简单地将一个异常转换为另一个异常,我认为使用recover是可以的。当您想尝试不同的策略来解决问题时,即当您希望获得成功的结果时,请使用 recoverWith。例如考虑下面使用 recoverWith

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

object futureRecoverWith extends App {

  case class Entity(id: Int, name: String)

  def selectFromDatabaseById(id: Int): Future[Entity] = Future(throw new RuntimeException("Boom"))
  def selectFromDatabaseByName(name: String): Future[Entity] = Future(Entity(42, "picard"))

  val entity =
    selectFromDatabaseById(42) recoverWith {
      case error =>
        println("Failed to get Entity by ID. Trying by name...")
        selectFromDatabaseByName("picard")
    }

  entity.map(println)
}

输出

Failed to get Entity by ID. Trying by name...
Entity(42,picard)

请注意我们如何尝试首先通过 id 获取实体,然后失败,我们尝试通过 name