2014-04-18 10 views
9

Ecco il problema, ho una libreria che ha un metodo di blocco return Prova [T]. Ma poiché è un blocco, vorrei renderlo non-blocking usando Future [T]. Nel blocco futuro, vorrei anche calcolare qualcosa che dipende dal valore di ritorno del metodo di blocco dell'origine.Qual è il modo migliore per avvolgere il blocco Prova [T] in Future [T] in Scala?

Ma se io uso qualcosa di simile al di sotto, poi il mio nonBlocking tornerà Futuro [Prova [T]] che è meno convincere dal Futuro [T] potrebbe rappresentare mancata [U] già, avrei preferito preferisco propagare l'eccezione a Future [T] è autonomo.

def blockMethod(x: Int): Try[Int] = Try { 
    // Some long operation to get an Int from network or IO 
    throw new Exception("Network Exception") } 
} 

def nonBlocking(x: Int): Future[Try[Int]] = future { 
    blockMethod(x).map(_ * 2) 
} 

Ecco quello che ho provato, mi basta usare .get metodo future {} blocco, ma non sono sicuro se questo è il modo migliore per farlo.

def blockMethod(x: Int): Try[Int] = Try { 
    // Some long operation to get an Int from network or IO 
    throw new Exception("Network Exception") } 
} 

def nonBlocking(x: Int): Future[Int] = future { 
    blockMethod(x).get * 2 
} 

È questo il modo corretto per farlo? O c'è un modo più idiomatico per convertire t Try [T] to Future [T]?

risposta

14

Ecco un esempio che non blocca, si noti che probabilmente si desidera utilizzare il proprio contesto di esecuzione e non contesto globale della Scala:

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

object Main extends App { 

    def blockMethod(x: Int): Try[Int] = Try { 
    // Some long operation to get an Int from network or IO 
    Thread.sleep(10000) 
    100 
    } 

    def tryToFuture[A](t: => Try[A]): Future[A] = { 
    future { 
     t 
    }.flatMap { 
     case Success(s) => Future.successful(s) 
     case Failure(fail) => Future.failed(fail) 
    } 
    } 

    // Initiate long operation 
    val f = tryToFuture(blockMethod(1)) 

    println("Waiting... 10 seconds to complete") 

    // Should return before 20 seconds... 
    val res = Await.result(f, 20 seconds) 

    println(res) // prints 100 
} 
+0

Se sai che stai bloccando, non si suppone per avvolgere il codice in 'blocking'? –

+0

Potrebbe essere utile il metodo 'fromTry' per questo? http://www.scala-lang.org/api/current/#scala.concurrent.Future$ – stefanobaghino

+0

Correzione automatica del mio commento precedente: 'fromTry' in realtà blocca, quindi non ci sono fortuna. – stefanobaghino

8

A mio parere: Prova & futuro è una costruzione monadica e idiomatica modo di composizione è monadica (per-comprensione):

che è necessario definire il trasformatore monade per il futuro [Prova [_]] (codice per la libreria):

case class FutureT[R](run : Future[Try[R]])(implicit e: ExecutionContext) { 
    def map[B](f : R => B): FutureT[B] = FutureT(run map { _ map f }) 
    def flatMap[B](f : R => FutureT[B]): FutureT[B] = { 
    val p = Promise[Try[B]]() 
    run onComplete { 
     case Failure(e)   => p failure e 
     case Success(Failure(e)) => p failure e 
     case Success(Success(v)) => f(v).run onComplete { 
     case Failure(e)   => p failure e 
     case Success(s)   => p success s 
     } 
    } 
    FutureT(p.future) 
    } 
} 

object FutureT { 
    def futureTry[R](run : => Try[R])(implicit e: ExecutionContext) = 
    new FutureT(future { run }) 

    implicit def toFutureT[R](run : Future[Try[R]]) = FutureT(run) 
    implicit def fromFutureT[R](futureT : FutureT[R]) = futureT.run 
} 

e l'uso ad esempio:

def blockMethod(x: Int): Try[Int] = Try { 
    Thread.sleep(5000) 
    if(x < 10) throw new IllegalArgumentException 
    else x + 1 
} 

import FutureT._ 

// idiomatic way :) 
val async = for { 
    x <- futureTry { blockMethod(15) } 
    y <- futureTry { blockMethod(25) }    
} yield (x + y) * 2 // possible due to using modan transformer 

println("Waiting... 10 seconds to complete") 

val res = Await.result(async, 20 seconds) 
println(res) 

// example with Exception 
val asyncWithError = for { 
    x <- futureTry { blockMethod(5) } 
    y <- futureTry { blockMethod(25) }    
} yield (x + y) * 2 // possible due to using modan transformer 

// Can't use Await because will get exception 
// when extract value from FutureT(Failure(java.lang.IllegalArgumentException)) 
// no difference between Failure produced by Future or Try 
asyncWithError onComplete { 
    case Failure(e) => println(s"Got exception: $e.msg") 
    case Success(res) => println(res) 
} 
// Output: 
// Got exception: java.lang.IllegalArgumentException.msg 
+0

c'è un bug da qualche parte. cambiando 'blockMethod (15)' da 'blockMethod (5)' si ottiene un'eccezione che termina il programma, e l'ultima riga 'println (res)' non viene eseguita. –

+0

Grazie David, ho perso la descrizione del comportamento di eccezione, aggiorno la mia risposta con l'esempio di eccezione. – Yuriy

Problemi correlati