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

sam*_*amz 20 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)
      } 
    }
})
Run Code Online (Sandbox Code Playgroud)

它在99%的时间内工作正常,但有时可能会失败(无关紧要的原因,这不是问题).

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

recover { case _ => BadRequest("")}
Run Code Online (Sandbox Code Playgroud)

但这并没有让我从错误中恢复过来.
我在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)
Run Code Online (Sandbox Code Playgroud)

这将按预期返回1.
我目前用以下方法包装Async:

try{
  Async {...}
} catch {
  case _ => BadRequest("")
} 
Run Code Online (Sandbox Code Playgroud)

这就抓住了错误.

我在网上浏览了一些Scala的未来文档,我很困惑为什么恢复不适合我.

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

Jam*_*per 42

为什么它失败实际上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("")}
  }
})
Run Code Online (Sandbox Code Playgroud)

所以,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) }
Run Code Online (Sandbox Code Playgroud)

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

因此,解决方法是换您的来电UserDao().getStuffOf(userId = userId).toList()在try catch块,或者找出为什么它的失败在任何你所呼叫,并捕获异常出现,并返回一个失败的未来.