2013-10-17 6 views
19

Ich benutze Scala, Play Framework 2.1.x und reactivemongo Treiber.Warum erkennt die Wiederherstellung von Future keine Ausnahmen?

Ich habe einen API-Aufruf:

def getStuff(userId: String) = Action(implicit request => { 
    Async { 
     UserDao().getStuffOf(userId = userId).toList() map { 
     stuffLst => Ok(stuffLst) 
     } 
    } 
}) 

Es funktioniert gut 99% der Zeit, aber es kann manchmal nicht (spielt keine Rolle, warum, das ist nicht das Problem ist).

Ich wollte in einem Fall eines Fehlers erholen, so dass ich hinzugefügt:

recover { case _ => BadRequest("")} 

Aber das hat mich nicht erholen von Fehlern.
habe ich versucht, das gleiche Konzept auf der scala Konsole und es funktionierte:

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) 

Dieses 1 kehrt wie erwartet.
Ich wickelte derzeit die Async mit:

try{ 
    Async {...} 
} catch { 
    case _ => BadRequest("") 
} 

Und die Fehler fängt.

Ich ging über einige Scala's Future Docs im Netz und ich bin verwirrt, warum Erholung für mich nicht funktioniert hat.

Weiß jemand warum? Was vermisse ich, um es zu klären?

Antwort

40

Warum es scheitert, ist 100% wichtig. Wenn wir den Code über eine Anzahl von Zeilen Code zu verbreiten, werden Sie verstehen, warum:

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("")} 
    } 
}) 

So UserDao().getStuffOf(userId = userId).toList() kehren Sie eine Zukunft. Eine Zukunft repräsentiert etwas, das vielleicht noch nicht passiert ist. Wenn dieses Objekt eine Ausnahme auslöst, können Sie diese Ausnahme bei der Wiederherstellung behandeln. In Ihrem Fall jedoch tritt der Fehler auf, bevor die Zukunft überhaupt erstellt wird, der UserDao().getStuffOf(userId = userId).toList() Aufruf wirft eine Ausnahme, die keine Zukunft zurückgibt. Daher wird der Aufruf zur Wiederherstellung der Zukunft niemals ausgeführt. Es ist äquivalent dies in dem Scala repl zu tun:

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) } 

Offensichtlich das funktioniert nicht, da man nie die Zukunft in erster Linie erstellt beacuse die Ausnahme vor dem Code ausgelöst wurde die Zukunft geschehen zu schaffen.

So ist die Lösung entweder auf Ihren Anruf zu UserDao().getStuffOf(userId = userId).toList() in einem Try-catch-Block wickeln, oder herausfinden, warum es in Ermangelung ist welche Methode Sie aufrufen, und die Ausnahme dort zu fangen, und kehren ein ausgefallenes Zukunft.

+0

was genau ist ein „failed Zukunft“? – ps0604

+0

@ ps0604: 'Future.failed ()' –

4

Wenn Sie eine neuere Version des Spiels zB 2.2.x haben, können Sie dies tun:

def urlTest() = Action.async { 
    val holder: WSRequestHolder = WS.url("www.idontexist.io") 
    holder.get.map { 
     response => 
     println("Yay, I worked") 
     Ok 
    }.recover { 
     case _ => 
     Log.error("Oops, not gonna happen") 
     InternalServerError("Failure") 
    } 
}