如何处理使用推导式和错误恢复的错误场景

  • 本文关键字:错误 恢复 何处理 处理 scala
  • 更新时间 :
  • 英文 :


目前我的错误处理不像我想的那样工作,这就是我想做的:

  1. UserApi。插入失败,返回错误,不要继续
  2. WorkApi。insert失败,调用UserApi.delete
  3. 返回错误
  4. WorkApi。assign失败,调用WorkApi.delete和UserApi.delete后返回错误

总的来说,UserApi。Insert被调用,如果成功,继续执行#2。如果WorkApi。插入成功,继续。等等,如果当前步骤失败,你必须扭转。

对于失败的Api调用,返回最相关的错误也是很重要的。

如果所有调用都成功,我想返回第一个调用的值,该值是User

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 = for {
insertResp <- UserApi.insert(u1)
workInsertResp <- WorkApi.insert(w1)
workAssignResp <- WorkApi.assign(w1)
} yield insertResp
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。我没有使用其他框架,只是简单的Scala。

https://scastie.scala-lang.org/OV4Ax58qQ1S3R3fFUikSbw

我相信这就是你所说的。

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
val resp: Future[Either[MyError,User]] =
UserApi.insert(u1).flatMap{_.fold(
{err => Future.successful(Left(err))}
,usr => WorkApi.insert(w1).flatMap{_.fold(
{err => UserApi.delete(u1); Future.successful(Left(err))}
, _  => WorkApi.assign(w1).map{_.fold(
{err => WorkApi.delete(w1); UserApi.delete(u1); Left(err)}
, _  => Right(usr)
)}
)}
)}
. . . //and the rest of your code

测试:

import scala.concurrent.duration.DurationInt
concurrent.Await.result(resp, 9999.millis)
//res0: Either[MyError,User] = Right(User(u1))

正如你所看到的,你当前的代码设计并不适合你所布置的任务。

首先,我建议不要这样混合FutureEitherFuture有自己的表示失败的方式,并且在Future中包装Either意味着您将需要处理失败的Future情况和EitherLeft情况,这可能导致一些令人困惑的代码。

在问题提供的代码中,没有异步执行,所以使用Future是多余的,您可以直接使用Either类型。但是,我假设您希望用实际(异步)API调用的方法替换这些方法,在这种情况下,您将希望使用Future而不使用EitherFuture要求失败值扩展Throwable,所以这需要更改MyError:

case class MyError(name: String) extends Exception(name)

其次,像Future(Right(user))Future(Left(MyError("UserApi.insert")))那样使用Future.apply进行非阻塞构造并不是一个好的做法。这并不明显,但这实际上导致Right(user)作为隐式执行上下文中的任务被调度,而不是在当前线程上同步计算。当结果不重要时,最好使用Future.successfulFuture.failed来创建完整的Future

有了这些变化,新的方法实现如下:
object UserApi {
def insert(user: User): Future[User] =
if (user.name == "u1") Future.successful(user)
else Future.failed(MyError("UserApi.insert"))
def delete(user: User): Future[String] =
Future.successful("UserApi.delete")
}
object WorkApi {
def insert(work: Work): Future[Work] =
if (work.name == "w1") Future.successful(work)
else Future.failed(MyError("WorkApi.insert"))
def delete(work: Work): Future[Work] =
Future.successful(work)
def assign(work: Work): Future[Work] =
if (work.name == "w1") Future.successful(work)
else Future.failed(MyError("WorkApi.assign"))
}