2014-04-18 13 views
9

Oto jest problem, mam bibliotekę, która ma powrót do metody blokowania Spróbuj [T]. Ale ponieważ jest to blokowanie, chciałbym uczynić to bez blokowania za pomocą Future [T]. W przyszłym bloku chciałbym także obliczyć coś, co zależy od wartości zwracanej przez metodę blokowania źródła.Jaki jest najlepszy sposób na blokowanie blokowania Spróbuj [T] w Przyszłości [T] w Scali?

Ale jeśli użyję czegoś takiego jak poniżej, mój nonBlocking zwróci Future [Spróbuj [T]], który jest mniej przekonany, ponieważ Future [T] może reprezentować Failure [U] już wolałbym propagować wyjątek do Future [T] jest self.

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) 
} 

Oto, co starałem, wystarczy użyć metody .get w future {} bloku, ale nie jestem pewien, czy jest to najlepszy sposób, aby to zrobić.

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 
} 

Czy to jest właściwy sposób? Lub jest bardziej scala idiomatyczny sposób konwersji t Spróbuj [T] na przyszłość [T]?

Odpowiedz

14

Oto przykład, że nie blokuje, trzeba pamiętać, że prawdopodobnie chcesz użyć własnego kontekstu wykonania, a nie globalny kontekst 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

Jeśli wiesz, że blokujesz, czy nie powinieneś owijać kodu w 'blokowanie'? –

+0

Czy metoda "fromTry" może być przydatna? http://www.scala-lang.org/api/current/#scala.concurrent.Future$ – stefanobaghino

+0

Samokorygujący mój poprzedni komentarz: 'fromTry' faktycznie blokuje, więc nie ma szczęścia. – stefanobaghino

8

Moim zdaniem: Spróbuj & Przyszłość jest monadycznego budowa i idiomatyczne sposobem jest skład jednowartościowy (do-zrozumienia):

że trzeba zdefiniować transformator monada dla przyszłości [Try [_]] (kod dla biblioteki):

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 
} 

i przykład użycia:

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

gdzieś jest błąd. zmiana 'blockMethod (15)' przez 'blockMethod (5)' powoduje wyjątek kończący program, a ostatnia linia 'println (res)' nie jest wykonywana. –

+0

Dziękuję David, przegapiłem opis zachowania wyjątków, zaktualizuj moją odpowiedź za pomocą przykładu wyjątku. – Yuriy

Powiązane problemy