Convert a Java Future to a Scala Future
Asked Answered
F

4

27

I have a Java Future object which I would like to convert into a Scala Future.

Looking at the j.u.c.Future API, there is nothing much that I could use other than the isDone method. Is this isDone method blocking?

Currently this is what I have in my mind:

val p = Promise()
if (javaFuture.isDone())
  p.success(javaFuture.get)

Is there a better way to do this?

Fatherhood answered 27/1, 2016 at 9:23 Comment(0)
R
23

How about just wrapping it (I'm assuming there's an implicit ExecutionContext here):

val scalaFuture = Future {
    javaFuture.get
}

EDIT:

A simple polling strategy could look like this (java.util.Future => F):

def pollForResult[T](f: F[T]): Future[T] = Future {
    Thread.sleep(500)
    f
  }.flatMap(f => if (f.isDone) Future { f.get } else pollForResult(f))

This will check if the Java future is done every 500ms. Obviously the total blocking time is the same as above (rounded up to the nearest 500ms) but this solution will allow other tasks to be interleaved in the same thread of the ExecutionContext.

Recommendatory answered 27/1, 2016 at 9:26 Comment(9)
Yeah! I already wrap it, but was just thinking if there might be a better way to do as I understand that the get operation on a Java future is a blocking operation!Fatherhood
Yes, but the resulting Scala Future is not. That is, the "wrapping" operation is non-blocking.Recommendatory
This is true, but while this will not block the current thread, this still blocks a thread from the ExecutionContext's pool of threads.Decalcify
@RégisJean-Gilles Sure, but not knowing the context, I think this solution is fine. The only other thing that could be done is polling for the result, but that may be unnecessary here.Recommendatory
It might be fine, but it's important to be clear about the thread blocking happening. That cannot be overlooked, it must be consciously assessed whether it's OK or not. It is best to be sure nobody gets the impression that wrapping a blocking call into Future{ ... } will magically remove any blocking.Decalcify
@pablochan, how does polling help with anything? Thread.sleep(500) blocks the same thread in the same way as a blocking javaFuture.get call, doesn't it?Cacophonous
Yes, sleep blocks the thread, but in a more predictable way than javaFuture.get. Once you call get, you block the thread until it's done. If the same ExecutionContext is used for other tasks, then a couple of long running Futures might clog it. I'm not saying that polling is objectively better than just wrapping the Future, but it might make sense in certain circumstances.Recommendatory
A slight improvement would be testing if your future is a CompletableFuture (or ListenableFuture if you use Guava) in which case blocking isn't necessary. for CompletableFuture you can use FutureConverters, for ListenableFuture it should be simple to write an equivalent (or maybe a library exists).Guaco
I did this originally but it ended up causing deadlock in my app. So I ended up converting the future into a CompletableFuture and using the scala conversion methods to convert it to a scala future.Declination
H
27

Starting Scala 2.13, the standard library includes scala.jdk.FutureConverters which provides Java to Scala CompletableFuture/Future implicit conversions:

import scala.jdk.FutureConverters._

// val javaFuture = java.util.concurrent.CompletableFuture.completedFuture(12)
val scalaFuture = javaFuture.asScala
// scalaFuture: scala.concurrent.Future[Int] = Future(Success(12))
Hyperpyrexia answered 30/3, 2019 at 8:40 Comment(2)
Please note that this only works with subtypes of java.util.concurrent.CompletionStage. It does not work for java.util.concurrent.Future.Freely
FutureConverts operates on the CompletableFuture and CompletionStage types, not the Future type. You're not answering OP's question.Goggleeyed
R
23

How about just wrapping it (I'm assuming there's an implicit ExecutionContext here):

val scalaFuture = Future {
    javaFuture.get
}

EDIT:

A simple polling strategy could look like this (java.util.Future => F):

def pollForResult[T](f: F[T]): Future[T] = Future {
    Thread.sleep(500)
    f
  }.flatMap(f => if (f.isDone) Future { f.get } else pollForResult(f))

This will check if the Java future is done every 500ms. Obviously the total blocking time is the same as above (rounded up to the nearest 500ms) but this solution will allow other tasks to be interleaved in the same thread of the ExecutionContext.

Recommendatory answered 27/1, 2016 at 9:26 Comment(9)
Yeah! I already wrap it, but was just thinking if there might be a better way to do as I understand that the get operation on a Java future is a blocking operation!Fatherhood
Yes, but the resulting Scala Future is not. That is, the "wrapping" operation is non-blocking.Recommendatory
This is true, but while this will not block the current thread, this still blocks a thread from the ExecutionContext's pool of threads.Decalcify
@RégisJean-Gilles Sure, but not knowing the context, I think this solution is fine. The only other thing that could be done is polling for the result, but that may be unnecessary here.Recommendatory
It might be fine, but it's important to be clear about the thread blocking happening. That cannot be overlooked, it must be consciously assessed whether it's OK or not. It is best to be sure nobody gets the impression that wrapping a blocking call into Future{ ... } will magically remove any blocking.Decalcify
@pablochan, how does polling help with anything? Thread.sleep(500) blocks the same thread in the same way as a blocking javaFuture.get call, doesn't it?Cacophonous
Yes, sleep blocks the thread, but in a more predictable way than javaFuture.get. Once you call get, you block the thread until it's done. If the same ExecutionContext is used for other tasks, then a couple of long running Futures might clog it. I'm not saying that polling is objectively better than just wrapping the Future, but it might make sense in certain circumstances.Recommendatory
A slight improvement would be testing if your future is a CompletableFuture (or ListenableFuture if you use Guava) in which case blocking isn't necessary. for CompletableFuture you can use FutureConverters, for ListenableFuture it should be simple to write an equivalent (or maybe a library exists).Guaco
I did this originally but it ended up causing deadlock in my app. So I ended up converting the future into a CompletableFuture and using the scala conversion methods to convert it to a scala future.Declination
B
13

For those reading this question now, if you're using Scala 2.13 and above, use:

import scala.jdk.FutureConverters._

And convert using completableFuture.asScala

If you're using Scala 2.12 and below, use

import scala.compat.java8.FutureConverters._

And convert using: toScala(completableFuture) or completableFuture.toScala

Also, in Scala 2.12 make sure you're using the correct artifact:

org.scala-lang.modules:scala-java8-compat_2.12:0.9.0

Now, if for some reason what you have is actually a Future and not CompletableFuture, which should be a rare case nowadays, please follow first one of those answers: Transform Java Future into a CompletableFuture

Baguette answered 22/8, 2019 at 10:5 Comment(1)
FutureConverts operates on the CompletableFuture and CompletionStage types, not the Future type. You're not answering OP's question.Goggleeyed
C
8

Use FutureConvertors (built-in util in Scala) for conversion of Java Future to Scala Future.

Consider an example for converting Java Future[Int] to Scala Future[Int] ::

import java.util.concurrent.CompletableFuture
import scala.compat.java8.FutureConverters

val javaFuture: java.util.concurrent.Future[Int] = ??? 
// Some method call which returns Java's Future[Int]

val scalaFuture: Future[Int] = 
FutureConverters.toScala(CompletableFuture.supplyAsync(new Supplier[Int] {
      override def get(): Int = javaFuture.get 
    }))

Similar we can do for any custom type instead of Int.

Crow answered 27/12, 2018 at 5:6 Comment(1)
They are good if you have a CompletableFuture. But the code you give is just a pointlessly more complicated version of @pablochan's answer.Guaco

© 2022 - 2024 — McMap. All rights reserved.