如何不抛出异常?

时间:2018-03-06 02:49:19

标签: scala slick slick-3.0

我有以下Slick代码,给定一个id返回一个客户(如果存在)。如果出现问题(例如连接丢失),Failure子句将抛出异常:

   def read (id: Int): Future[Option[Customer]] = {
      val db = // .... 
      val customers = TableQuery[CustomerDB]
      val action = customers.filter(_.id === id).result
      val future = db.run(action.asTry)
      future.map{
        case Success(s) => 
          if (s.length>0)
            Some(s(0))
          else
            None
        case Failure(f) => throw new Exception (f.getMessage)
      }
   }

现在,我的理解是,在Scala中,不应使用try / catch / finally异常,而应使用Try。此外,不应抛出任何异常。但是如果没有抛出异常,如何通知上层发生了问题?

3 个答案:

答案 0 :(得分:3)

Future本身已经尝试了内部。所以,我想说你需要扁平化(你的代码有点复杂,我简化了):

  future.flatMap {
    case Success(s) => Future.successful(s.headOption)
    case Failure(f) => Future.failed(f)
  }

结果Futurefailed状态时通知调用者执行失败(包装原始异常)。否则,成功。

答案 1 :(得分:1)

报告错误的正确方法是使用Either。

trait Error
case class NotFound(id: Int) extends Error
case class QueryFailed(msg: String) extends Error

def read (id: Int): Future[Either[Error, Customer]] = {
   val db = // .... 
   val customers = TableQuery[CustomerDB]
   val action = customers.filter(_.id === id).result
   val future = db.run(action.asTry)
   future.map{
      case Success(s) => 
         if (s.length>0)
           Right(s(0))
         else
           Left(NotFound(id))

      case Failure(f) => Left(QueryFailed(f.getMessage))
   }
}

答案 2 :(得分:0)

好的,一般情况下,你可以使用Future.successful或Future.failed(msg:String)来“发信号”你得到了值的上层(也就是调用方法)。

更好的方法

然而,一个好的方法是在失败的情况下对Future使用.recoverWith{}

例如:

def getUserFromCloud (userId: String): Future[String] = Future{
  cloudProviderApi.getUsername(userId)
}.recoverWith{
  Future.failed(s"$userId does not exist.")    
}  

调用方法怎么样?

您只需使用recover:

映射成功和欠码并处理错误
getUserFromCloud("test").map(_ => {
    //In case of success
}).recover{
    //In case of failure, like return BadRequest. 
}

有关recoverrecoverWith的更多内容,如果您有兴趣:Scala recover or recoverWith