2016-04-15 52 views
2

我希望以下代码在方法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只在未来发生。我仍然无法解释我所看到的行为。 (我想知道它是否与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处应执行我的意料,(它得到执行),并返回的响应应该“妈的事,但我还行”

Actuality 该播放框架返回InternalServerError,我看到我的控制台上的错误堆栈。我看到printlin(“这被打印”)正在执行。

不知道发生了什么事。任何见解?

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

我核实,内心戏框架的控制器调用的时候,问题只发生(我用打2.5)。作为一个独立的scala程序,everthing按预期工作。我相信游戏错误处理会捕获未经处理的异常并打印堆栈跟踪。我认为这应该只发生在开发环境中。

+1

尝试'val f1 = callfuture1(); val f2 = callfuture2; f = {x < - f1; y < - f2}产生y'。不同的是,在你的情况下,期货正在顺序执行(y等待callfuture1完成)。 – chuwy

+0

“堆栈停止”是什么意思?什么是“标准内部错误”?你怎么知道'onFailure'没有执行?什么是实际的错误? – Dima

+1

另外。请注意'onFailure'只能用于副作用。它不能改变'Future'中包含的异常。如果你想摆弄异常,你需要使用'转换'或'恢复' – Dima

回答

2

如果callfuture1抛出“未来之外”,则会发生这种情况。 您的理解力脱成这样:

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

如果callfuture2马上(而不是返回一个失败的未来),你仍然会落得失败的未来,因为callfuture2被称为内Future.flatMap,它捕获异常抛出并将其转化为失败的期货(与Future.map相同)。

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

一般来说,您应该尽量避免使用返回Future的方法,并且可以使用也会导致错误。 这意味着如果callfuture1做了任何可以抛出的东西,它应该抓住它并在失败的将来返回异常。

UPDATE:关于你关于你如何预期更新“妈的事,但我还是确定”返回:

由于已经由迪马在评论暗示,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") 
    } 
} 
+0

我更新了问题。我解决了将来被抛出异常的问题,但我仍然看到我无法解释的行为:(任何见解?) – konquestor

+0

请参阅我的更新 –

0

似乎里面callfuture1()你不包装未来的构造函数中所有的过程是怎样的

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

,但你的代码似乎是

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

是因为它是未来之外,您错误直接丢入您的程序代码

+0

答案是不是一个地方提问,使用意见 –

+0

我知道,但我现在没有足够的声誉 – bthuillier