2014-04-13 7 views
22

Ho un futuro [T] e voglio mappare il risultato, sia in caso di successo sia in caso di fallimento.Mappa di un futuro sia per il successo sia per il fallimento

Ad esempio, qualcosa come

val future = ... // Future[T] 
val mapped = future.mapAll { 
    case Success(a) => "OK" 
    case Failure(e) => "KO" 
} 

Se uso map o flatmap, sarà mappare solo successi futuri. Se utilizzo recover, mapperà solo i futures non riusciti. onComplete esegue una richiamata ma non restituisce un futuro modificato. Transform funzionerà, ma prende 2 funzioni piuttosto che una funzione parziale, quindi è un po 'più brutto.

So che potrei fare un nuovo Promise, e completa che con onComplete o onSuccess/onFailure, ma speravo ci fosse qualcosa che mancava che mi avrebbe permesso di fare quanto sopra con un unico PF.

risposta

28

Modifica 2017-09-18: A partire da Scala 2.12, esiste un metodo transform che prende uno Try[T] => Try[S]. Così si può scrivere

val future = ... // Future[T] 
val mapped = future.transform { 
    case Success(_) => Success("OK") 
    case Failure(_) => Success("KO") 
} 

Per 2.11.x, il vale sotto ancora:

per quanto ne so, non è possibile farlo direttamente con un unico PF. E transform trasforma Throwable => Throwable, quindi non ti sarà d'aiuto neanche. Il più vicino è possibile ottenere fuori dalla scatola:

val mapped: Future[String] = future.map(_ => "OK").recover(_ => "KO") 

Detto questo, l'implementazione del mapAll è banale:

implicit class RichFuture[T](f: Future[T]) { 
    def mapAll[U](pf: PartialFunction[Try[T], U]): Future[U] = { 
    val p = Promise[U]() 
    f.onComplete(r => p.complete(Try(pf(r)))) 
    p.future 
    } 
} 
+3

Sì, potrei facilmente scriverlo, mi chiedevo solo se qualcosa del genere fosse in agguato da qualche parte. – monkjack

+1

Ciao @espenhw, nel secondo paragrafo della tua risposta, intendevi in ​​realtà 'val mapped: Future [String] = future.map (_ =>" OK "). Recover {case _ =>" KO ")' –

+0

Mi faceva impazzire non capire quale combinatore usare, specialmente dal momento che. Completo ha esattamente la firma che voglio, ma non mi dà un nuovo futuro. Userò questa soluzione; Grazie. – kornfridge

3

In una prima fase, si potrebbe fare qualcosa di simile:

import scala.util.{Try,Success,Failure} 

val g = future.map(Success(_):Try[T]).recover{ 
    case t => Failure(t) 
}.map { 
    case Success(s) => ... 
    case Failure(t) => ... 
} 

dove T è il tipo del risultato futuro. Quindi è possibile utilizzare una conversione implicita per aggiungere questa struttura la Future tratto come un nuovo metodo:

implicit class MyRichFuture[T](fut: Future[T]) { 
    def mapAll[U](f: PartialFunction[Try[T],U])(implicit ec: ExecutionContext): Future[U] = 
    fut.map(Success(_):Try[T]).recover{ 
     case t => Failure(t) 
    }.map(f) 
} 

che implementa la sintassi che state cercando:

val future = Future{ 2/0 } 
future.mapAll { 
    case Success(i) => i + 0.5 
    case Failure(_) => 0.0 
} 
3

Sia mappa e flatMap varianti:

implicit class FutureExtensions[T](f: Future[T]) { 
    def mapAll[Target](m: Try[T] => Target)(implicit ec: ExecutionContext): Future[Target] = { 
    val p = Promise[Target]() 
    f.onComplete { r => p success m(r) }(ec) 
    promise.future 
    } 

    def flatMapAll[Target](m: Try[T] => Future[Target])(implicit ec: ExecutionContext): Future[Target] = { 
    val promise = Promise[Target]() 
    f.onComplete { r => m(r).onComplete { z => promise complete z }(ec) }(ec) 
    promise.future 
    } 
} 
2

Dal momento che è possibile utilizzare Scala 2.12transform per mappare entrambi i casi:

future.transform { 
     case Success(_) => Try("OK") 
     case Failure(_) => Try("KO") 
} 

Hai anche transformWith se si preferisce utilizzare un Future invece di un Try. Controlla il documentation per i dettagli.

Problemi correlati