2013-08-02 46 views
26

我正在编写围绕java库的小型scala包装。ListenableFuture to scala未来

Java库的目的QueryExecutor曝光2种方法:

  • 执行(查询):结果
  • asyncExecute(查询):ListenableFuture [结果]

ListenableFuture在这种情况下是一个来自番石榴图书馆。

我想我的scala包装返回Future [结果]而不是java对象,但我不知道什么是最好的实现方式。这里有两个解决方案,我想出了:

future { 
    executor.execute(query) 
} 

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 

我想知道哪种方法是最好的。我的直觉是,第一个在返回Future时,仍然会阻塞一个线程,而执行调用等待响应,第二个看起来应该是非阻塞的。对每种方法的优缺点有何评论?

+2

假设您有4个处理器。在这种情况下,默认的'ExecutionContext'由4名工人组成。每个'future {executor.execute(query)}'会阻止1个worker,所以4个“futures”将完全阻止你的程序。你可以为阻塞操作创建额外的'ExecutionContext',但是会有一些开销。 – senia

+0

谢谢@senia,这就是我的想法。第一个代码从调用者的角度来看是异步的,但仍然会阻塞ExecutionContext的线程,而第二个代码实际上是非阻塞的(假设asyncExecute使用非阻塞IO)。我觉得这是一个非常基本的问题,但我对Promises不是很熟悉。 – vptheron

+1

我发现这对于类似(甚至可能相同)的需求是有帮助的:https://github.com/eigengo/activator-akka-cassandra/blob/master/src/main/scala/core/cassandra.scala – Gavin

回答

37

第二个选项是最好的,它保持一切异步。但是...你可以做一个更好的和抽象的解决方案到一个可重用的模式:

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

然后,您可以直接电话咨询:

executor.asyncExecute(query).asScala 
+1

真棒解决方案。不能相信我的ListenableFuture接口代码有多清洁 –

+2

非常好。使用'Promise [T]'可能会更抽象' – raam86

+0

不能不指出,toPromise实际上会返回一个Future。 ;)否则,爱它! –