为什么Future的恢复没有捕获异常?

时间:2013-10-17 16:08:26

标签: scala playframework reactivemongo

我正在使用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包装了Async:

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

这会抓住错误。

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

有谁知道为什么?我会错过什么来解决它?

2 个答案:

答案 0 :(得分:41)

为什么它失败实际上是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) }

显然这不起作用,因为你从来没有创造过未来,因为在创建未来的代码发生之前抛出了异常。

所以解决方案是在try catch块中包含对UserDao().getStuffOf(userId = userId).toList()的调用,或者找出它为什么在你调用的方法中失败,并在那里捕获异常,并返回失败的未来。

答案 1 :(得分:4)

如果你有更高版本的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")
    }
}