我希望以下代码在其中一个方法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按预期工作。我相信播放错误处理捕获未经处理的异常并打印堆栈跟踪。我认为这应该只发生在开发环境中。
答案 0 :(得分:2)
如果callfuture1
抛出&#34;未来之外&#34;会发生这种情况。
理解你的理解是因为:
val f = callfuture1.flatMap{ x =>
callfuture2.map{ y =>
y
}
}
如果callfuture2
立即抛出(而不是返回失败的未来),您仍将以失败的未来结束,因为在callfuture2
内调用了Future.flatMap
,这会捕获异常并且把它们变成失败的未来(Future.map
相同)。
callfuture1
的情况有所不同:如果它立即抛出,则没有封闭Future.map
或Future.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)
}
所以因为它在未来之外,你的错误直接投入到你的程序代码中