使用惯用的Scala处理错误

时间:2016-08-10 12:00:17

标签: scala asynchronous

我正在为聊天机器人编写HTTPS服务,发现自己正在处理很多期货和期权。通常,如果Option返回None或者Future失败,我想记录异常并将用户重置回到开头。以下是我如何实现这一目标的玩具示例:

(for {
  user <- userService.retrieve(userId)
  userPet <- Future(user.userPet.get)
  _ <- sendTextAsJson(s"You're holding a $userPet!")
} yield {}).recover {
  case ex: Exception =>
    log.error(ex.toString)
    fail
}

这样可以正常工作但是在Future中包装东西感觉有点奇怪,因此它们的异常被吞并并在recover块中处理。包含空的产量块也感觉很奇怪。还有更好的方法吗?

4 个答案:

答案 0 :(得分:1)

你基本上做的是使用onSuccess或onFailure来检索期货结果。您也可以尝试尝试。

有一个底层功能的例子。 http://www.scala-lang.org/api/2.9.3/scala/util/Try.html

我可能会建议你这篇文章:http://docs.scala-lang.org/overviews/core/futures.html我无法用几句话总结那里所说的内容。但是,如果您查看右侧的目录,Futures将解释会发生什么以及如何处理它在Excepetions中说明。这是惯用的方式。

答案 1 :(得分:0)

我认为这不是太糟糕,假设userService.retrieve()首先返回Future。在这种情况下,我个人更倾向于使用地图来使事情变得更加明确:

val futureMsg = userService.retrieve(userId)
.map(user => sendTextAsJson(s"You're holding a ${user.userPet.get}!")
.recover {
  case NonFatal(ex) => //Look it up ;)
    log.error(ex.toString)
    fail
}

你现在有了Future [Unit]来做你想做的事。

答案 2 :(得分:0)

我同意你的观点,这是对理解的尴尬。我就是这样写的:

import scala.util.control.NonFatal

userService.retrieve(userId)
  .map(_.userPet.get)
  .map(userPet => s"You're holding a $userPet!")
  .flatMap(sendTextAsJson)
  .recover {
    case NonFatal(ex) =>
      log.error(ex.toString)
      fail
  }

答案 3 :(得分:0)

查看sendTextAsJson和失败函数,您似乎希望在将来完成时产生副作用。执行Option.get后我不会使用map,而是查看Futures onComplete方法来处理未来的成功或处理异常抛出。我不确定这种方式和恢复是如何不同虽然如此仔细检查。我确实喜欢@sascha10000链接到scala doc期货。自从我读到这篇文章以来已经很长时间了,但它是我记忆中的一个很好的资源

使用onComplete实现代码的示例:

import scala.concurrent.Future
import scala.util.{Failure, Success}
import scala.concurrent.ExecutionContext.Implicits.global

object UserFuture extends App {

  def fail = println("failed")

  def sendTextAsJson(message: String): Unit = println(message)

  case class User(userPet: Option[String])

  object userService {

    def userPet = Some("dog")

    val someUser = User(userPet)

    def retrieve(userId: Int): Future[User] = Future {
      someUser
    }
  }

  def getPet(userId: Int): Unit = {
    userService.retrieve(userId)
      .map(user => user.userPet.get)
      .onComplete {
        case Success(userPet) => sendTextAsJson(s"You're holding a $userPet!")
        case Failure(ex) => println(ex.toString); fail
      }
  }

  getPet(1)

  Thread.sleep(10000) // I forgot how to use Await. This is just here to be able to make sure we see some printouts in the console.

}