2013-08-02 12 views
26

Jestem w trakcie pisania małego opakowania scala wokół biblioteki Java.ListenableFuture to scala Future

Biblioteka Java ma QueryExecutor obiektu poddawanie 2 metodami:

  • wykonanie (hasła): Wynik
  • asyncExecute (zapytanie) ListenableFuture [wynik]

ListenableFuture w tym kontekście ten z biblioteki guawy.

Chcę, aby moje opakowanie scala zwróciło wartość Future [Result] zamiast obiektu java, ale nie jestem pewien, jaki jest najlepszy sposób na jego implementację. Tu są 2 rozwiązania wymyśliłem:

future { 
    executor.execute(query) 
} 

i

val p = promise[Result] 
val guavaFuture = executor.asyncExecute(query) 

Futures.addCallback(guavaFuture, new FutureCallback[Result] { 
    def onFailure(t: Throwable) { 
    p.failure(t) 
    } 

    def onSuccess(result: Result) { 
    p.success(result) 
    } 
}) 

p.future 

Zastanawiam się, która metoda jest najlepsza. Moją intuicją jest, że pierwsza, zwracając Przyszłość, będzie nadal blokować wątek, podczas gdy wywołanie wykonania czeka na odpowiedź, druga wygląda tak, jakby nie była blokowana. Wszelkie uwagi na temat zalet/wad każdej metody?

+2

Załóżmy masz 4 procesory. W tym przypadku domyślny "ExecutionContext" składa się z 4 pracowników. Każde 'przyszłe {executor.execute (zapytanie)} blokuje 1 pracownika, więc 4" futures "całkowicie zablokuje twój program. Można utworzyć dodatkowe 'ExecutionContext' dla operacji blokowania, ale będzie trochę narzut. – senia

+0

Dzięki @senia, tak właśnie myślałem. Pierwszy kod jest asynchronizowany z punktu widzenia wywołującego, ale nadal blokuje wątek Kontekstu Wykonania, podczas gdy drugi jest naprawdę niezablokowany (zakładając, że asyncExecute używa niezablokowania IO). Czuję, że jest to bardzo podstawowe pytanie, ale nie jestem obeznany z Obietnicami. – vptheron

+1

Uważam, że jest to pomocne w podobnym (lub możliwym nawet identycznym) wymogu: https://github.com/eigengo/activator-akka-cassandra/blob/master/src/main/scala/core/cassandra.scala – Gavin

Odpowiedz

37

Druga opcja jest najlepsza, zachowuje wszystko asynchronicznie. ale ... można zrobić jeden lepszy i abstrakcyjne roztwór do wielokrotnego użytku wzoru:

implicit class RichListenableFuture[T](lf: ListenableFuture[T]) { 
    def asScala: Future[T] = { 
    val p = Promise[T]() 
    Futures.addCallback(lf, new FutureCallback[T] { 
     def onFailure(t: Throwable): Unit = p failure t 
     def onSuccess(result: T): Unit = p success result 
    }) 
    p.future 
    }  
} 

Następnie można po prostu zadzwonić:

executor.asyncExecute(query).asScala 
+1

Niesamowite rozwiązanie. Nie mogę uwierzyć, jak bardzo czysty jest mój kod interfejsu ListenableFuture z tym niejawnym –

+2

Bardzo ładny. może być jeszcze bardziej abstrakcyjna za pomocą 'Promise [T]' – raam86

+0

Nie mogę pomóc, ale zwróć uwagę, że toPromise faktycznie zwraca Przyszłość. ;) W przeciwnym razie, uwielbiam to! –