如何在Scala中使用for推导式处理错误场景并实现失败回滚
实现方案
你当前使用的for推导仅能处理Future执行成功的场景,无法在Either返回Left时触发自定义回滚逻辑,因此我们手动拆分每一步的执行、错误判断、回滚流程,确保失败时先执行对应回滚操作,再返回原始触发失败的错误。
完整实现代码
import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import scala.util.{Success, Failure} val u1 = User("u1") val w1 = Work("w1") // 核心业务逻辑 val resp: Future[Either[MyError, User]] = UserApi.insert(u1).flatMap { // UserApi.insert失败,无前置操作,直接返回对应错误 case Left(err) => Future.successful(Left(err)) case Right(user) => WorkApi.insert(w1).flatMap { // WorkApi.insert失败,先执行UserApi.delete回滚,再返回对应错误 case Left(err) => UserApi.delete(user).map(_ => Left(err)) case Right(work) => WorkApi.assign(w1).flatMap { // WorkApi.assign失败,依次执行WorkApi.delete、UserApi.delete回滚,再返回对应错误 case Left(err) => for { _ <- WorkApi.delete(work) _ <- UserApi.delete(user) } yield Left(err) // 所有操作执行成功,返回UserApi.insert的结果 case Right(_) => Future.successful(Right(user)) } } } println("ending...") resp onComplete { case Success(r) => println(r) case Failure(t) => println(t) } case class User(name: String) case class Work(name: String) case class MyError(name: String) object UserApi { def insert(user: User): Future[Either[MyError, User]] = if (user.name == "u1") Future(Right(user)) else Future(Left(MyError("UserApi.insert"))) def delete(user: User): Future[Either[MyError, String]] = Future(Right("UserApi.delete")) } object WorkApi { def insert(work: Work): Future[Either[MyError, Work]] = if (work.name == "w1") Future(Right(work)) else Future(Left(MyError("WorkApi.insert"))) def delete(work: Work): Future[Either[MyError, Work]] = Future(Right(work)) def assign(work: Work): Future[Either[MyError, Work]] = if (work.name == "w1") Future(Right(work)) else Future(Left(MyError("WorkApi.assign"))) }
逻辑说明
- 完全基于Scala 2.13原生API实现,无第三方依赖
- 错误返回始终为触发失败的对应API的原始错误,不会被回滚操作的结果覆盖
- 回滚操作严格按需求顺序执行,不会遗漏前置操作的回滚
- 所有流程执行成功时返回
User类型的插入结果,符合需求
内容的提问来源于stack exchange,提问作者nolambda
相关产品推荐
相关产品推荐

