无法处理未来失败的异常

时间:2016-04-15 08:29:39

标签: scala exception-handling playframework-2.0 future

我希望以下代码在其中一个方法callfuture1()callfuture2()引发异常时返回自定义消息。我的理解是,如果未来任何一个失败,f将是一个失败的未来。

然而,当callfuture1抛出异常时。 f.onFailure未执行。相反,我看到调用堆栈在callFuture1()中的代码行停止,其中发生异常并返回标准internalError。为什么会这样?

val f = for {
 x <- callfuture1()
 y <- callfuture2()
} yield y

f.onFailure {
 //send an internalserver error with some custom message
}

f.map {
 //send data back
}

====更新====

我从回复中看到,潜在的问题是Exception被抛到了Future之外,因此我的代码无法捕捉到失败的未来。 所以我改变了代码,以便Exception只在未来发生。我仍然无法解释我所看到的行为。 (我想知道它是否与Play框架有关。)

def controllerfunction(id: String) = Action.async{

  val f = for{
    x <- callfuture1(id)
    y <- callfuture2(x)
  } yield y

  y.onFailure{case t => 
    println("This gets printed");
    Ok("shit happened, but i am still ok")}

  y.map{resp:String => Ok(resp)}

}

def callfuture1(id: String):Future[Obj1] = {
  for {
    val1 <- callfuture1.1(id)
    val2 <- callfuture1.2(val1)
  } yield val2
}

def callfuture1.2:Future[Obj3] = Future{
  thrown new Exception("TEST ME");
}

def callfuture 1.1:Future[Obj4] = {...}
def callfuture2: Future[String] = {....}

展望。 方法callfuture1.2在将来抛出一个异常,所以我的期望是onFailure应该被执行,(它确实被执行),并且返回的响应应该&#34; Shit发生了,但我仍然可以&#34;

现状 play框架返回InternalServerError,我在控制台上看到错误堆栈。我看到printlin(&#34;这被打印&#34;)正在执行。

不能理解发生了什么。任何见解?

====更新2 =====

我验证了问题只发生在play框架的控制器内部(我正在使用play 2.5)。作为一个独立的scala程序,everthing按预期工作。我相信播放错误处理捕获未经处理的异常并打印堆栈跟踪。我认为这应该只发生在开发环境中。

2 个答案:

答案 0 :(得分:2)

如果callfuture1抛出&#34;未来之外&#34;会发生这种情况。 理解你的理解是因为:

val f = callfuture1.flatMap{ x =>
  callfuture2.map{ y =>
    y
  }
}

如果callfuture2立即抛出(而不是返回失败的未来),您仍将以失败的未来结束,因为在callfuture2内调用了Future.flatMap,这会捕获异常并且把它们变成失败的未来(Future.map相同)。

callfuture1的情况有所不同:如果它立即抛出,则没有封闭Future.mapFuture.flatMap将其变为失败的未来。

通常,您应该尽量避免使用返回Future的方法,并且会抛出错误。 这意味着如果callfuture1执行任何可以抛出的内容,它应该捕获该异常并在失败的未来中转换异常然后返回。

更新:关于您的预期更新情况&#34;狗屎发生了,但我仍然可以&#34;退回:

正如Dima在评论中已经暗示的那样,Future.onFailure只能用于副作用。期货是不变的。如果您想从失败的异常中恢复,则无法修改原始(失败)的未来,您实际可以做的就是将其转换为新的未来。 看看Future.recover。它完全符合您的需要,即它允许通过匹配失败的结果(如果有的话)并将其转换为成功的未来来转换输入未来。它相当于一个catch子句,但对于期货。具体地说,你真正想要做的是这样的事情:

def controllerfunction(id: String) = Action.async{
  val f = for{
    x <- callfuture1(id)
    y <- callfuture2(x)
  } yield y

  f.map{ resp: String => 
    Ok(resp)
  }.recover{
    case t: Throwable => 
      println("This gets printed");
      Ok("shit happened, but i am still ok")
  }
}

答案 1 :(得分:0)

似乎在callfuture1()里面你没有将所有进程包装在Future构造函数中,如

def callfuture1(): Future[?] = Future {
  val x = ...
  x
}

但您的代码似乎是

def callfuture1(): Future[?] = {
  val x = ... // some error happen here
  Future(x)
}

所以因为它在未来之外,你的错误直接投入到你的程序代码中