2013-08-15 7 views
28

Qual è il modo più pulito per il Exception di un errore Future in scala?Mappa l'eccezione di un futuro fallito

Dire che ho:

import scala.concurrent._ 
import scala.concurrent.ExecutionContext.Implicits.global 

val f = Future { 
    if(math.random < 0.5) 1 else throw new Exception("Oh no") 
} 

se il futuro riesce con 1, mi piacerebbe continuare a che, se non riesce vorrei cambiare il Exception a un diverso Exception.

Il meglio che ho potuto venire in mente è di trasformare, tuttavia, che mi obbliga a fare una funzione inutile per il caso di successo:

val f2 = f.transform(s => s, cause => new Exception("Something went wrong", cause)) 

C'è qualche motivo non v'è alcuna mapFailure(PartialFunction[Throwable,Throwable])?

+7

transform è la giusta via da percorrere. non è necessario creare la funzione s => s, basta passare in '' identity'' –

+0

Cheers. Non conoscevo la funzione 'identità'. Sono sicuro che ci saranno più volte in cui ciò sarà utile. – theon

risposta

26

C'è anche:

f recover { case cause => throw new Exception("Something went wrong", cause) } 

Dal Scala 2.12 si può fare:

f transform { 
    case s @ Success(_) => s 
    case Failure(cause) => Failure(new Exception("Something went wrong", cause)) 
} 

o

f transform { _.transform(Success(_), cause => Failure(new Exception("Something went wrong", cause)))} 
+1

Sebbene si tratti di una sintassi molto chiara, dobbiamo comunque "lanciare" la nuova eccezione, invece di ottenere la mappatura di 'Throwable' su' Throwable'. Esiste un combinatore per questo? – owensmartin

+0

@owensmartin Vedi la mia risposta aggiornata. :) –

13

Si potrebbe provare recoverWith come in:

f recoverWith{ 
    case ex:Exception => Future.failed(new Exception("foo", ex)) 
} 
+0

Future.failed non viene valutato su un ExeecutionContext: http://www.scala-lang.org/api/current/#scala.concurrent.Future$ vedi '' failed'' –

+0

@ViktorKlang, Devo essere confuso allora. Avevo preso questa ipotesi osservando il metodo 'impl.KeptPromise' class 'onComplete'. In là, ci sono le due linee 'val preparedEC = executor.prepare; (nuovo CallbackRunnable (preparedEC, func)). executeWithValue (completedAs) '. Supponevo che ciò implicasse che, anche se stavamo dando un valore esplicito che per qualche motivo stava ancora colpendo l'esecutore. Mi è sempre sembrato strano e immagino sia perché lo stavo interpretando male. Grazie per il testa a testa. – cmbaxter

+0

Grazie per le risposte. La mia unica esitazione nell'usare 'recover' /' recoverWith' è che quando lo vedo presumo immediatamente che l'intenzione è di recuperare da un errore con un risultato positivo. Forse sono solo io. Anche così sono buone alternative, grazie! – theon