在 scala 中处理用 Try 包裹的未来

Dealing with a Future wrapped with Try in scala

我有一个 returns 未来的回购方法:

def insert(newUser: User): Future[User]

此方法可能因不同原因而失败,但是,我希望能够捕获这些原因之一并重新抛出其他原因。 如果我只是用 Try

结束调用
Try(userRepository.insert(usr)) match {
      case Success(newUserFuture) => newUserFuture

      case Failure(e) if e.getCause.getMessage.contains("duplicate key") => {
        logger.warn(e.getMessage)
        throw DuplicateEntityException("duplicate user record")
      }

      case Failure(e) => {
        logger.error(e.getMessage)
        throw e
      }
    }

结果,我得到 Try[Future[User]],在这种情况下,两种失败都不会发生,因为 Future 已成功创建。 在这一点上,我不确定如何处理它。理想情况下,我想从中得到 Future[Either[String, User]](或类似的东西)。

我应该如何处理这种情况,还是我的方向完全错误?

根据建议编辑 可能的解决方案 我试过这样的事情:

userRepository.insert(usr).map(newUser => Right(newUser)) recoverWith {
      case e: PSQLException if e.getMessage.contains("duplicate key") =>
        Future.successful(Left("duplicate user record"))
    }

我认为你可以颠倒你的方法,在这种情况下使用守卫来稍微清理一下代码。首先是让 userRepository.insert(usr) return 一个 Future[Try[User]],这样你就可以映射未​​来,例如

def insert(user: User): Future[Try[User]]

然后可以这样使用:

userRepository.insert(user).map {
    case Success(insertedUser) => Right(insertedUser)
    case Failure(ex) if ex.getCause.getMessage.contains("duplicate key") => throw DuplicateEntityException("duplicate user record")
    case Failure(ex) => throw ex
}

这将是一个 Future[User],您可以将回调附加到(请参阅 http://docs.scala-lang.org/overviews/core/futures.html)以对用户执行其他操作或处理异常。

Future 将为您处理异常。您不需要用 Try.

包装未来的代码

Future 也完成 Try 的工作。

不需要这行代码

Try(userRepository.insert(usr))

我希望你的插入方法是这样的

def insert(newUser: User): Future[User] = Future { doSomeDBCall() }

在上面的方法中。如果 doSomeDBCall() 失败。该异常将被 future 安全地捕获,并将作为 Failure(exception) 传递给调用者。所以你不需要用 Try

包裹 Future

捕获特定异常并抛出其他异常(传播其他异常)。

您可以使用 recoverWith 处理异常并传播您不感兴趣的异常。

在下面的代码中 SQLException 被处理,其他异常被传播给调用者。

userRepository.insert(usr).recoverWith { 
 case ex: SQLException => Future.successful(defaultValue)
 case ex => Future.failed(ex)
}

尝试这样的事情:

def futureToFutureTry[T](f: Future[T]): Future[Try[T]] =
   f.map(Success(_)).recover {
     case e: Throwable => {
      Failure(e)
   }
}

...
userRepository.insert(usr) map futureToFutureTry

用于检查

def checkOperations[A](operations: List[Try[A]]): Future[List[A]] = {
   Future {
      if (!operations.filter(_.isFailure).isEmpty) {
       // there were some error

      } else {
       // all operations are ok
     }
}