繁体   English   中英

使用Either进行Scala Future错误处理

[英]Scala Future error handling with Either

我正在编写API的包装器,我想对应用程序问题进行错误处理。 每个请求都返回一个Future,所以为了做到这一点,我看到了两个选项:使用Future[Either]或使用exception来立即失败。

以下是两种情况的代码段, response是HTTP请求返回的未来:

  def handleRequestEither: Future[Either[String, String]] = {
    response.map {
      case "good_string" => Right("Success")
      case _ => Left("Failed")
    }
  }

  def handleRequest: Future[String] = {
    response.map {
      case "good_string" => "Success"
      case _ => throw new Exception("Failed")
    }
  }

以下是在两种情况下获得结果的片段:

handleRequestEither.onComplete {
  case Success(res) =>
    res match {
      case Right(rightRes) => println(s"Success $res")
      case Left(leftRes) => println(s"Failure $res")
    }
  case Failure(ex) =>
    println(s"Failure $ex")
}

handleRequest.onComplete {
  case Success(res) => println(s"Success $res")
  case Failure(ex) => println(s"Failure $ex")
}

我不喜欢使用异常,但是使用Future[Either]使之后获得响应变得更加冗长,如果我想将结果映射到另一个对象,它会变得更加复杂。 这是要走的路,还是有更好的选择?

让我解释一下Erik Meijer并考虑下表:

在此输入图像描述

然后考虑一下语言结构的这两个特性: arity (它是聚合一个还是多个项?)和模式 (阻塞读操作时同步,直到准备好或异步时)。

所有这些都意味着Try构造和块管理同步生成结果的块的成功或失败。 您可以控制资源是否提供正确答案而不会遇到问题(例外情况描述的问题)。

另一方面, Future是一种异步Try 这意味着它在没有发现任何问题(例外)然后通知其订户时成功完成。 因此, 我不认为你应该有一个未来Either在这种情况下 ,这是你第二次handleRequest实现是利用期货的正确方法。

最后,如果扰乱你的是抛出异常,你可以遵循Promises的方法:

def handleRequest: Future[String] = {
   val p = Promise[String]
   response.map {
      case "good_string" => p.success("Success")
      case _ => p.failure(new Exception("Failed"))
    }
   p.future
}

要么:

case class Reason(msg: String) extends Exception

def handleRequest: Future[String] = {
   val p = Promise[String]
   response.map {
      case "good_string" => p.success("Success")
      case _ => p.failure(Reason("Invalid response"))
    }
   p.future
}

我宁愿使用你的第二种方法。

您可以使用该特殊类型: EitherTscalaz库

它适用于scalaz增强版的Either\\/

它可以将任何monad和\\/组合转换为单个monad。 因此,对于scala.concurent.Future使用scalaz实例,您可以实现所需的混合。 如果你愿意,你可以进一步使用monad变形金刚。 如果您有兴趣,请阅读这个美丽的博客。

这里没有美化,但为您使用scalaz 7.1示例:

import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scalaz._
import scalaz.std.scalaFuture._
import EitherT._
import scala.concurrent.ExecutionContext.Implicits.global

object EitherFuture {
  type ETFS[X] = EitherT[Future, String, X]

  val IntResponse = "result (\\d+)".r

  def parse(response: Future[String]) =
    eitherT(response map {
      case IntResponse(num) ⇒ \/-(num.toInt)
      case _ ⇒ -\/("bad response")
    })

  def divideBy2(x: Validation[String, Int]) = 
    x.ensure("non divisible by 2")(_ % 2 == 0).map(_ / 2)

  def handleResponse(response: Future[String]) = for {
    num ← parse(response).validationed(divideBy2)
  } yield s"half is $num"

  def main(args: Array[String]) {
    Map(
      'good → "result 10",
      'proper → "result 11",
      'bad → "bad_string"
    ) foreach { case (key, str) ⇒
      val response = Future(str)
      val handled = handleResponse(response)
      val result = Await.result(handled.run, Duration.Inf)

      println(s"for $key response we have $result")
    }
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM