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

Scala Cats EitherT异常捕获差异及循环优雅容错方案问询

问题1解答

原因在于异常抛出的时机和线程上下文完全不同:

  1. 当calculateOne抛出异常时,这个异常是在主线程同步执行阶段抛出的——调用calculateOne()是直接在try代码块里同步执行的,还没进入Future的异步逻辑,所以外层的try-catch能直接捕获到这个异常。
  2. 而calculateTwo的调用是在EitherT的flatMap逻辑中(也就是for推导的后续步骤),这部分代码属于异步执行逻辑:当calculateTwo抛出异常时,异常会被封装到Future.failed实例中,但外层的try-catch只负责同步执行创建EitherT的代码,并不会等待异步Future完成,且异步线程的异常不会冒泡到主线程的try-catch调用栈里,因此无法被捕获。

注:你的calculateTwo方法目前缺少返回值,实际运行会编译报错,需补全返回逻辑(比如Future.failed(new RuntimeException("error from two")))。


问题2解答

要统一处理同步/异步异常且不混用try-catch,可以借助Cats的EitherT和ScalaFuture的错误处理工具,把所有异常都纳入Either的左值体系,实现统一的错误处理:

优化后的代码示例

package com.oxo.test

import cats.data.EitherT
import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.Try

object FutureError extends App {

  val processingList = List(1, 2, 3)

  processingList.foreach { x =>
    // 用EitherT封装整个流程,统一处理所有异常
    val processingFlow = for {
      result1 <- EitherT(calculateOne(x))
      result2 <- EitherT(calculateTwo(x))
      result3 <- EitherT(calculateThree(x))
      sum = result1 + result2 + result3
    } yield sum

    // 统一捕获所有异常(同步抛出+Future失败),转换成Either左值
    val handledFlow = processingFlow.recover {
      case e: Exception => s"Processing failed: ${e.getMessage}"
    }

    // 处理最终的Either结果
    handledFlow.value.map {
      case Right(sum) => println(s"Success processing $x: sum = $sum")
      case Left(errMsg) => println(s"Error processing $x: $errMsg")
    }
  }

  // 封装同步异常到Future[Either],避免直接抛出
  private def calculateOne(input: Int): Future[Either[String, Int]] = {
    Future.fromTry(Try(throw new RuntimeException("error from one")))
      .map(_.left.map(_.getMessage))
  }

  private def calculateTwo(input: Int): Future[Either[String, Int]] = {
    Future.successful(Right(input + 2))
  }

  private def calculateThree(input: Int): Future[Either[String, Int]] = {
    Future.successful(Right(input + 3))
  }
}

核心思路

  1. 封装同步异常:用Future.fromTry(Try(...))把可能抛出同步异常的逻辑包裹起来,确保同步异常被转换成Future[Either]的左值,而不是直接在主线程抛出。
  2. 统一错误处理:利用EitherT.recover方法捕获所有异常(包括异步Future的失败和同步抛出的异常),将其转换成统一的错误信息字符串,纳入Either的左值体系。
  3. 单一结果处理入口:最后只需要处理Either的Right(成功)和Left(错误)两种情况即可,无需单独使用try-catch或Future.recover。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 19:54:54