2016-04-15 8 views
2

Ich möchte den folgenden Code eine benutzerdefinierte Nachricht zurückgeben, wenn eine der Methoden callfuture1() oder callfuture2() eine Ausnahme auslöst. Mein Verständnis war, wenn einer der Zukunft fehlschlägt, f wäre eine gescheiterte Zukunft.nicht handhabbare Ausnahme von zukünftigem Fehler

Wenn jedoch callfuture1 eine Ausnahme auslöst. f.onFailure wird nicht ausgeführt. Stattdessen sehe ich, dass der Aufruf-Stack an der Codezeile in callFuture1() angehalten wurde, wo Ausnahme aufgetreten ist und ein interner Standardfehler zurückgegeben wird. Warum passiert das?

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

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

f.map { 
//send data back 
} 

==== ==== Update

i aus den Antworten zu sehen, dass potenzielles Problem ist, dass Ausnahme außerhalb der Zukunft geworfen wird und damit nicht mein Code Zukunft das nicht gelang fangen . Also habe ich den Code so geändert, dass Ausnahme nur innerhalb der Zukunft auftritt. Ich kann das Verhalten, das ich sehe, immer noch nicht erklären. (Ich frage mich, ob es etwas mit Play-Rahmen zu tun.)

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] = {....} 

Erwartung. Verfahren callfuture1.2 löst eine Ausnahme in der Zukunft, so meine Erwartung onFailure ausgeführt werden soll, (die ausgefuehrt wird,) und die Antwort zurück sollte „Scheiße passiert ist, aber ich bin immer noch in Ordnung“

Aktualität Das Play-Framework gibt InternalServerError zurück und ich sehe den Fehler-Stack auf meiner Konsole. Ich sehe, dass der Printlin ("Dies wird gedruckt") ausgeführt wird.

Kann nicht verstehen, was passiert. Irgendwelche Einsichten?

==== Update 2 =====

prüfte ich, dass das Problem nur, wenn innerhalb Steuerung des Spiels Rahmen genannt geschieht (i unter Verwendung von 2,5 bin spielen). Als eigenständiges Scala-Programm funktioniert alles wie erwartet. Ich glaube, dass die Behandlung von Spielfehlern die nicht verarbeitete Ausnahme abfängt und die Stapelverfolgung druckt. Ich denke, das sollte nur in der Entwicklungsumgebung passieren.

+1

Versuchen Sie 'val f1 = callfuture1(); val f2 = Anrufzukunft2; f = für {x <- f1; y <- f2} Ausbeute y '. Der Unterschied besteht darin, dass Futures in Ihrem Fall nacheinander ausgeführt werden (y wartet auf den Abschluss von callfuture1). – chuwy

+0

Was meinen Sie mit "stack stopped"? Was ist "interner Standardfehler"? Woher weißt du, dass onFailure nicht ausgeführt wird? Was ist der eigentliche Fehler? – Dima

+1

Auch. Beachten Sie, dass OnFailure nur für Nebenwirkungen verwendet werden kann. Es kann die in der "Zukunft" enthaltene Ausnahme nicht ändern. Wenn Sie mit der Ausnahme fiedeln möchten, müssen Sie 'transform' oder' recover' verwenden. – Dima

Antwort

2

Dies kann passieren, wenn callfuture1 "außerhalb einer Zukunft" wirft. Ihr für das Verständnis in dieser entzuckert:

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

Wenn callfuture2 rechts wegwirft (im Gegensatz zu einer ausgefallenen Zukunft zurückkehrt), werden Sie noch mit einer gescheiterten Zukunft am Ende, weil callfuture2 innerhalb Future.flatMap aufgerufen, die Ausnahmen abfängt und verwandelt sie in gescheiterte Futures (selbe für Future.map).

Anders ist die Situation für callfuture1: wenn es richtig wegwirft, gibt es keine Future.map oder Future.flatMap umschließen, um sie in einer gescheiterten Zukunft.

Im Allgemeinen sollten Sie versuchen, eine Methode zu vermeiden, die eine Future zurückgibt und auch einen Fehler werfen kann. Das bedeutet, wenn callfuture1 alles tut, was werfen kann, sollte es das fangen und die Ausnahme in einer gescheiterten Zukunft drehen, die Sie dann zurückgeben.

UPDATE: Bezüglich Ihrer Update darüber, wie Sie erwarteten „Scheiße passiert ist, aber ich bin immer noch in Ordnung“ zurückgegeben werden:

Wie bereits von Dima in einem Kommentar angedeutet, Future.onFailure nur für Nebenwirkungen verwendet werden kann, . Futures sind unveränderbar. Wenn Sie von einer fehlgeschlagenen Ausnahme wiederherstellen möchten, gibt es keine Möglichkeit, die ursprüngliche (fehlgeschlagene) Zukunft zu ändern, und Sie können sie nur in eine neue Zukunft transformieren. Werfen Sie einen Blick auf Future.recover. Es macht genau das, was Sie brauchen, nämlich es ermöglicht, eine Eingabe-Zukunft zu transformieren, indem das gescheiterte Ergebnis (falls vorhanden) abgeglichen und in eine erfolgreiche Zukunft umgewandelt wird. Es entspricht einer Fangklausel, aber für Futures. Konkret, was Sie wirklich tun wollten, ist etwas in der Art:

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

Ich aktualisierte die Frage . Ich behandelte das Problem der Ausnahme, die außerhalb der Zukunft geworfen wird, aber ich sehe immer noch das Verhalten, das ich nicht erklären kann :(. irgendwelche Einblicke? – konquestor

+0

Bitte beachten Sie mein Update –

0

Es scheint, dass innerhalb callfuture1() Sie alle Prozess nicht wie

in der Zukunft Konstruktor Einwickeln scheint
def callfuture1(): Future[?] = Future { 
    val x = ... 
    x 
} 

aber Ihr Code

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

so zu sein, weil es außerhalb der Zukunft ist, Ihre Fehler wirft direkt in Ihren Programmcode

+0

Antworten ist kein Ort, um Fragen zu stellen, verwenden Sie Kommentare –

+0

Ich weiß, aber ich habe nicht genug Ruf für jetzt – bthuillier