2013-08-02 23 views
26

Tôi đang trong quá trình viết một trình bao bọc scala nhỏ xung quanh một thư viện java.ListenableFuture to scala Future

Thư viện java có QueryExecutor đối tượng phơi bày 2 phương pháp:

  • thực hiện (query): Kết quả
  • asyncExecute (query): ListenableFuture [Kết quả]

ListenableFuture trong bối cảnh này là một từ thư viện ổi.

Tôi muốn trình bao bọc scala của tôi trả về một [Kết quả] tương lai thay vì đối tượng java, nhưng tôi không chắc cách tốt nhất để thực hiện điều đó là gì. Dưới đây là 2 giải pháp tôi đã đưa ra:

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 

tôi tự hỏi phương pháp nào là tốt nhất. Trực giác của tôi là cái đầu tiên, trong khi trả về một tương lai, sẽ vẫn chặn một luồng trong khi cuộc gọi thực hiện chờ phản hồi, cái thứ hai trông giống như nó thực sự không bị chặn. Bất kỳ bình luận về ưu/khuyết điểm của mỗi phương pháp?

+2

Hãy giả sử bạn có 4 bộ vi xử lý. Trong trường hợp này mặc định 'ExecutionContext' bao gồm 4 công nhân. Mỗi 'tương lai {executor.execute (truy vấn)}' chặn 1 công nhân, vì vậy 4 "tương lai" sẽ chặn hoàn toàn chương trình của bạn. Bạn có thể tạo thêm 'ExecutionContext' để chặn các hoạt động, nhưng sẽ có một số phí. – senia

+0

Cảm ơn @senia, đó là những gì tôi nghĩ. Mã đầu tiên là không đồng bộ từ quan điểm của người gọi nhưng sẽ vẫn chặn một chuỗi của ExecutionContext, trong khi mã thứ hai thực sự không chặn (giả sử rằng asyncExecute sử dụng non blocking IO). Tôi cảm thấy như đây là một câu hỏi rất cơ bản nhưng tôi không quen lắm với Promises. – vptheron

+1

Tôi thấy điều này hữu ích trong một yêu cầu tương tự (hoặc thậm chí có thể giống hệt): https://github.com/eigengo/activator-akka-cassandra/blob/master/src/main/scala/core/cassandra.scala – Gavin

Trả lời

37

Tùy chọn thứ hai là tốt nhất, nó giữ mọi thứ không đồng bộ. nhưng ... bạn có thể làm một tốt hơn và trừu tượng dung dịch vào một mô hình tái sử dụng:

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

Sau đó, bạn có thể chỉ cần gọi:

executor.asyncExecute(query).asScala 
+1

Giải pháp tuyệt vời. Không thể tin rằng mã giao tiếp ListenableFuture của tôi sạch hơn bao nhiêu là với điều này một cách rõ ràng –

+2

Rất đẹp. thậm chí có thể trừu tượng hơn bằng cách sử dụng 'Promise [T]' – raam86

+0

Không thể không chỉ ra rằng toPromise thực sự trả về một tương lai. ;) Nếu không, hãy yêu nó! –