2013-07-28 11 views
21

Ist es möglich, scala.util.Try und scala.concurrent.Future zu verketten? Beide bieten effektiv die gleiche monadische Schnittstelle, aber der Versuch, sie zu verketten, führt zu einem Kompilierungsfehler.Scala - Chaining Futures Versuche Blöcke?

Zum Beispiel. Angesichts der beiden Signaturen unter

def someFuture:Future[String] = ??? 
def processResult(value:String):Try[String] = ??? 

ist es möglich, etwas wie folgt zu tun?

val result = for(a <- someFuture; b <- processResult(a)) yield b; 
result.map { /* Success Block */ } recover { /* Failure Block */ } 

Dies führt offensichtlich in einem Compiler-Fehler, weil Zukunft und versuchen nicht in der Lage sind flatMapp'ed zusammen zu sein.

Es wäre aber ein nettes Feature, sie verketten zu können - ist das überhaupt möglich? Oder muss ich sie zu einem Future [Try [String]] kombinieren?

(Insbesondere bin ich daran interessiert, einen einzigen 'recover' Block zu haben, um Ausnahmen entweder in der Zukunft oder den Versuch zu fangen).

Antwort

27

Wenn Sie mit einem Problem wie diesem konfrontiert sind, bei dem Sie verschiedene Typen zum Verständnis verwenden möchten, können Sie versuchen, einen der Typen auszuwählen und den anderen Typ zuzuweisen. Für Ihre Situation, würde ich angesichts der einzigartigen Eigenschaften (async) von Futures, Future als den kleinsten gemeinsamen Nenner wählen und die Try auf die Future zuordnen. Sie können einfach tun, wie folgt aus:

val result = for{ 
    a <- someFuture 
    b <- tryToFuture(processResult(a)) 
} yield b 
result.map { /* Success Block */ } recover { /* Failure Block */ } 

def tryToFuture[T](t:Try[T]):Future[T] = { 
    t match{ 
    case Success(s) => Future.successful(s) 
    case Failure(ex) => Future.failed(ex) 
    } 
} 

Nun, wenn Sie dies eine sehr häufige Situation zu sein gefunden und Sie haben nicht wie ständig in der expliziten Umwandlung hinzufügen zu müssen, nehme ich an Sie die tryToFuture Methode als implizites definieren könnten auf einig Hilfsobjekt und importiert, wo wie dies erforderlich:

object FutureHelpers{ 
    implicit def tryToFuture[T](t:Try[T]):Future[T] = { 
    t match{ 
     case Success(s) => Future.successful(s) 
     case Failure(ex) => Future.failed(ex) 
    } 
    } 
} 

import FutureHelpers._ 
val result = for{ 
    a <- someFuture 
    b <- processResult(a) 
} yield b 
result.map { /* Success Block */ } recover { /* Failure Block */ } 

Denken sie daran, dass Future.success und Future.failed hat Auswirkungen Aufruf auf, was ExecutionContext in in Rahmen ist, dass es eine andere Aufgabe es unter der Haube vorlegen wird.

EDIT

Wie Viktor in den Kommentaren darauf hingewiesen, den Prozess ein Try zu einem Future der Umwandlung ist es noch einfacher, wenn Sie nur Future.fromTry wie von unten in dem aktualisierten Beispiel verwenden:

val result = for{ 
    a <- someFuture 
    b <- Future.fromTry(processResult(a)) 
} yield b 
result.map { /* Success Block */ } recover { /* Failure Block */ } 

Dies ist wahrscheinlich die beste Option, wenn Sie nicht mit Implits arbeiten oder Ihre eigene Konvertierungslogik erstellen.

+4

gibt es derzeit eine [Diskussion auf scala-user] (https://groups.google.com/d/topic/scala-user (Mu4_lZAWxz0/Diskussion) über genau dieses Problem der Umwandlung eines 'Try' in ein' Future'. Vielleicht sollte ein solcher Helfer irgendwo in der Standardbibliothek enthalten sein. – gourlaysama

+0

Future.fromTry? –

+0

@ViktorKlang, ja du hast Recht. Ich habe meine Antwort aktualisiert, um diesen Ansatz zu berücksichtigen. Danke für die Köpfe hoch. – cmbaxter

2

Wie wäre es

val result = for(a <- someFuture) yield for(b <- processResult(a)) yield b; 

Auch wenn es nicht ordentlich aussieht.

1
implicit def convFuture[T](ft: Future[Try[T]]): Future[T] = 
ft.flatMap { 
    _ match { 
    case Success(s) => Future.successful(s) 
    case Failure(f) => Future.failed(f) 
    } 
} 
2

Vielleicht ist das Problem ist alt, aber zur Zeit können Sie:

implicit def tryToFuture[T](t:Try[T]):Future[T] = Promise.fromTry(t).future 
1

Es ist auch

Future.fromTry(Try { ... }) 

So könnten Sie tun

val result = for { 
    a <- someFuture 
    b <- Future.fromTry(processResult(a)) 
} yield b; 
Verwandte Themen