2015-08-30 8 views
12

Ich möchte Fehlerbehandlung in meinem Spiel scala Web-Anwendung zu tun.Fehlerbehandlung Scala: Zukunft für Verständnis

Meine Anwendung spricht mit der Datenbank, um einige Zeilen zu holen, es folgt folgender Ablauf.

  1. erster Aufruf db einige Daten
  2. , um die Daten in ersten Anruf zu holen andere Daten von holen db
  3. eine Antwort bilden die empfangenen Daten von den letzten zwei db Anrufen verwenden.

Unten ist mein Pseudocode.

def getResponse(name: String) 
     (implicit ctxt: ExecutionContext): Future[Response] = { 
    for { 
     future1 <- callFuture1(name) 
     future2 <- callFuture2(future1.data) 
     future3 <- callFuture3(future1.data, future2.data) 
    } yield future3 
    } 

Jede Methode im obigen Verständnis gibt eine Zukunft zurück, die Signatur dieser Methoden ist wie folgt.

private def callFuture1(name: String) 
    (implicit ctxt: ExecutionContext): Future[SomeType1] {...} 

private def callFuture2(keywords: List[String]) 
    (implicit ctxt: ExecutionContext): Future[SomeType2] {...} 

private def callFuture3(data: List[SomeType3], counts: List[Int]) 
    (implicit ctxt: ExecutionContext): Future[Response] {...} 

Wie soll ich tun, Fehler/Fehlerbehandlung, in der folgenden Situation

  • Wenn callFuture1 nicht Daten aus der Datenbank zu holen. Ich möchte eine entsprechende Fehlerantwort mit Fehlermeldung zurückgeben. Seit callFuture2 wird nur nach callFuture1 ausgeführt. Ich möchte callFuture2 nicht ausführen, wenn callFuture1 fehlgeschlagen ist/Fehler und Fehlermeldung sofort zurückgegeben werden soll. (Das Gleiche gilt für callFuture2 und callFuture3)

--edit--

Ich versuche, eine entsprechende Fehlerreaktion von getResponse() -Methode zurück, wenn entweder der callFuture ausfällt und nicht auf nachfolgende gehen FutureCalls.

Ich habe versucht, die folgenden, basierend auf Peter Neyens Antwort, aber gab mir einen Laufzeitfehler ..

def getResponse(name: String) 
     (implicit ctxt: ExecutionContext): Future[Response] = { 
    for { 
     future1 <- callFuture1(name) recoverWith { 
     case e:Exception => return Future{Response(Nil,Nil,e.getMessage)} 
     } 
     future2 <- callFuture2(future1.data) 
     future3 <- callFuture3(future1.data, future2.data) 
    } yield future3 
    } 

Laufzeitfehler i

ERROR] [08/31/2015 02:09:45.011] [play-akka.actor.default-dispatcher-3] [ActorSystem(play)] Uncaught error from thread [play-akka.actor.default-dispatcher-3] (scala.runtime.NonLocalReturnControl) 
[error] a.a.ActorSystemImpl - Uncaught error from thread [play-akka.actor.default-dispatcher-3] 
scala.runtime.NonLocalReturnControl: null 

Antwort

18

Sie erhalten könnte die Future.recoverWith Funktion verwenden, anpassen die Ausnahme, wenn Future fehlgeschlagen ist.

val failed = Future.failed(new Exception("boom")) 
failed recoverWith { 
    case e: Exception => Future.failed(new Exception("A prettier error message", e) 
} 

Diese in einem etwas hässlichen zum Verständnis führen:

for { 
    future1 <- callFuture1(name) recoverWith { 
       case npe: NullPointerException => 
       Future.failed(new Exception("how did this happen in Scala ?", npe)) 
       case e: IllegalArgumentException => 
       Future.failed(new Exception("better watch what you give me", e)) 
       case t: Throwable => 
       Future.failed(new Exception("pretty message A", t)) 
      } 
    future2 <- callFuture2(future1.data) recoverWith { 
       case e: Exception => Future.failed(new Exception("pretty message B", e)) 
      } 
    future3 <- callFuture3(future1.data, future2.data) recoverWith { 
       case e: Exception => Future.failed(new Exception("pretty message C", e)) 
      } 
} yield future3 

Beachten Sie, dass Sie auch Ihre eigenen Ausnahmen definieren könnten statt Exception verwenden, wenn Sie als weitere Informationen hinzufügen mögen nur einen Fehler Botschaft.

Wenn Sie nicht mögen, feinkörnige Steuernachricht einer andere Fehlers in Abhängigkeit von den Throwable in dem gescheiterten Future (wie bei callFuture1) Sie Future mit einer impliziten Klasse bereichern könnten eine benutzerdefinierte Fehlermeldung setzen etwas einfacher:

implicit class ErrorMessageFuture[A](val future: Future[A]) extends AnyVal { 
    def errorMsg(error: String): Future[A] = future.recoverWith { 
    case t: Throwable => Future.failed(new Exception(error, t)) 
    } 
} 

Welche könnten Sie wie verwenden:

for { 
    future1 <- callFuture1(name) errorMsg "pretty A" 
    future2 <- callFuture2(future1.data) errorMsg "pretty B" 
    future3 <- callFuture3(future1.data, future2.data) errorMsg "pretty C" 
} yield future3 

In beiden Fällen unter Verwendung von errorMsg oder recoverWith direkt, y Sie verlassen sich immer noch auf Future, wenn also ein Future ausfällt, wird das folgende Futures nicht ausgeführt und Sie können direkt die Fehlermeldung innerhalb des fehlgeschlagenen Future verwenden.

Sie haben nicht angegeben, wie Sie die Fehlermeldungen behandeln möchten. Wenn Sie beispielsweise die Fehlermeldung verwenden möchten, um eine andere Response zu erstellen, können Sie recoverWith oder recover verwenden.

future3 recover { case e: Exception => 
    val errorMsg = e.getMessage 
    InternalServerError(errorMsg) 
} 
+0

Danke für diese Antwort. Es war hilfreich. Ich bin mir immer noch nicht sicher, ob das mein Problem löst. 1) Unter Verwendung des Ansatzes 1 (ohne implizite Klasse zu verwenden), wenn ich mit Future.failed (new Exception()) recovere, wird eine Ausnahme ausgelöst und meine Ausführung wird nicht fortgesetzt. Ich versuche, eine angemessene ResponseMessage zurückzugeben, wenn einer der zukünftigen Anruf fehlschlägt, ohne zu nachfolgenden zukünftigen calls (ich werde die Frage aktualisieren, um es klarer zu machen) – konquestor

+0

Future.fail (neue Ausnahme (...)) verursacht die Ausführung zu werfen eine Ausnahme. Stattdessen möchte ich eine entsprechende Fehlermeldung zurückgeben, wenn etwas fehlschlägt/Fehler in futurecall. – konquestor

+0

Versuchen Sie etwas wie das letzte Codebeispiel in meiner Antwort. –

1

Say future1, future2 und future3 Wurf Throwable Ausnahmen Future1Exception genannt, Future2Exception und Future3Exception sind. Dann können Sie entsprechende Fehler zurück Response von getResponse() Methode wie folgt:

def getResponse(name: String) 
      (implicit ctxt: ExecutionContext): Future[Response] = { 
    (for { 
    future1 <- callFuture1(name) 
    future2 <- callFuture2(future1.data) 
    future3 <- callFuture3(future1.data, future2.data) 
    } yield future3).recover { 
    case e: Future1Exception => 
     // build appropriate Response(...) 

    case e: Future2Exception => 
     // build appropriate Response(...) 

    case e: Future3Exception => 
     // build appropriate Response(...) 
    } 
} 

Laut Dokumentation Future.recover

Erstellt eine neue Zukunft, das passende throwable behandelt, dass diese Zukunft enthalten könnten.

Verwandte Themen