【问题标题】:Why does Future's recover not catch exceptions?为什么 Future 的恢复不能捕获异常?
【发布时间】:2013-10-26 05:51:55
【问题描述】:

我正在使用 Scala、Play Framework 2.1.x 和 reactivemongo 驱动程序。

我有一个 api 调用:

def getStuff(userId: String) = Action(implicit request => {
    Async {
      UserDao().getStuffOf(userId = userId).toList() map {
        stuffLst => Ok(stuffLst)
      } 
    }
})

它在 99% 的情况下都可以正常工作,但有时可能会失败(不管为什么,这不是问题)。

我想在出现错误的情况下恢复,所以我补充说:

recover { case _ => BadRequest("")}

但这并不能让我从错误中恢复过来。
我在 scala 控制台上尝试了相同的概念,它奏效了:

import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
var f = future { throw new Exception("") } map {_ => 2} recover { case _ => 1}
Await.result(f, 1 nanos)

这会按预期返回 1。
我目前将 Async 包装为:

try{
  Async {...}
} catch {
  case _ => BadRequest("")
} 

这会捕获错误。

我在网上浏览了一些 Scala 的 Future 文档,我很困惑为什么恢复对我不起作用。

有人知道为什么吗?我想解决什么问题?

【问题讨论】:

    标签: scala playframework reactivemongo


    【解决方案1】:

    为什么它失败实际上是 100% 重要的。如果我们将代码分布在多行代码中,您就会明白为什么:

    def getStuff(userId: String) = Action(implicit request => {
      Async {
        val future = UserDao().getStuffOf(userId = userId).toList()
        val mappedFuture = future.map {
          stuffLst => Ok(stuffLst)
        }
        mappedFuture.recover { case _ => BadRequest("")}
      }
    })
    

    所以,UserDao().getStuffOf(userId = userId).toList() 为您带来未来。未来代表可能尚未发生的事情。如果那个东西抛出异常,你可以在恢复中处理那个异常。但是,在您的情况下,错误发生在甚至在创建未来之前UserDao().getStuffOf(userId = userId).toList() 调用正在引发异常,而不是返回未来。所以恢复未来的调用永远不会被执行。相当于在 Scala repl 中这样做:

    import scala.concurrent._
    import scala.concurrent.duration._
    import ExecutionContext.Implicits.global
    var f = { throw new Exception(""); future { "foo" } map {_ => 2} recover { case _ => 1} }
    Await.result(f, 1 nanos) }
    

    显然这不起作用,因为您一开始就没有创建未来,因为在创建未来的代码发生之前引发了异常。

    因此,解决方案是将您对 UserDao().getStuffOf(userId = userId).toList() 的调用包装在 try catch 块中,或者找出在您调用的任何方法中失败的原因,并在那里捕获异常,并返回失败的 future。

    【讨论】:

    • 究竟什么是“失败的未来”?
    • @ps0604: Future.failed(<the exception you caught>)
    【解决方案2】:

    如果您有更高版本的 Play,例如 2.2.x,您可以这样做:

    def urlTest() = Action.async {
        val holder: WSRequestHolder = WS.url("www.idontexist.io")
        holder.get.map {
          response =>
            println("Yay, I worked")
            Ok
        }.recover {
          case _ =>
            Log.error("Oops, not gonna happen")
            InternalServerError("Failure")
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-24
      • 1970-01-01
      • 2021-03-16
      • 1970-01-01
      • 2012-08-27
      • 2021-05-13
      • 1970-01-01
      • 2020-07-31
      相关资源
      最近更新 更多