2016-04-15 50 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