You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala中Await.result异常处理问题:自定义异常未正确捕获

解决Scala For推导式中Await.result捕获自定义异常的问题

我来帮你排查这个问题——你遇到的核心问题是None.get抛出的异常并不是来自你在recoverWith里捕获的Repository调用失败,而是来自你在for推导式后续步骤中直接调用了.get方法。这个异常发生在Future成功返回None之后,完全绕开了你之前定义的recoverWith逻辑,所以外层catch只能抓到通用的Exception,而不是你期望的自定义异常。

问题根源拆解

咱们一步步看代码逻辑:

  1. 当rackRepository.getById(rack.id)成功返回None时,这个Future处于成功状态,所以你后面的recoverWith不会触发——recoverWith只处理Future的失败情况。
  2. 接下来你执行futureSeqGpuRow <- gpuRepository.getByRack(futureRackRow.get.id),这里直接调用futureRackRow.get,因为futureRackRow是None,立刻抛出NoSuchElementException(也就是你看到的None.get异常)。
  3. 这个异常会被Scala自动包装进当前Future的失败通道,但它已经不是你定义的RackException了,所以外层catch的自定义异常分支不会命中,只能走到通用的Exception分支。

解决方案:提前处理Option,避免.get

要解决这个问题,你需要在Future的处理流程中就把None的情况转换成自定义异常,而不是留到后面用.get硬取。具体可以这么做:

1. 把Option转换成Future的失败状态

在处理rackRepository.getById的结果时,用flatMap把Option[RackRow]转换成Future[RackRow]——如果是Some就返回成功的Future,如果是None就返回失败的Future(抛自定义异常)。这样不管是Repository调用失败,还是返回None,都会进入Future的失败通道,被你后续的recoverWith或者外层catch正确捕获。

2. 移除所有.get调用

既然已经把Option转换成了确定的RackRow,后续代码就不需要再用.get了,直接使用对象的属性即可。

修改后的完整代码

try {
  val futureResult = for {
    // 先处理Option:None直接转换成RackException的失败Future
    rackRow <- rackRepository.getById(rack.id).flatMap {
      case Some(row) => Future.successful(row)
      case None => Future.failed(RackException(s"Rack not found with id: ${rack.id}"))
    } recoverWith {
      // 捕获Repository调用时的异常,转换成自定义异常
      case e: Exception => Future.failed(RackException(s"Error on select Rack: ${e.getMessage}"))
    }
    // 现在直接用rackRow.id,不需要.get
    seqGpuRow <- gpuRepository.getByRack(rackRow.id) recoverWith {
      case e: Exception => Future.failed(GpuException(s"Error on select Gpu's from Rack: ${e.getMessage}"))
    }
  } yield (rackRow, seqGpuRow)
  
  println(2)
  val result = Await.result(futureResult, 20 seconds)
  
  // 这里rackRow已经是确定存在的,不需要再处理None情况
  val (rackRow, seqGpuRow) = result
  // 后续业务逻辑...
  
  Ok
} catch {
  case re: RackException => BadRequest(s"Rack Exception: ${re.getMessage}")
  case ge: GpuException => BadRequest(s"Gpu Exception: ${ge.getMessage}")
  case e: Exception => BadRequest(s"Exception found: ${e.getMessage}")
}

额外提醒

在Scala里,永远不要随意调用.get处理Option,除非你能100%保证它不会是None。尤其是在Future异步场景下,.get抛出的异常会被包装成通用的Exception,不仅难以定位问题,还会破坏你自定义异常的捕获逻辑。优先用模式匹配、flatMap、fold这类安全的方式处理Option。

内容的提问来源于stack exchange,提问作者Felipe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:29:40