scala

How to execute a future immediately


Say I have a future defined as follows

val fut = Future {
  println("Doing some work")
  Thread.sleep(5000)
  println("Work done")
}

I am accessing this future later in time but would like to have it execute on startup rather than when I reference fut

I read somewhere that it can be executed immediately by doing:

scala.concurrent.ExecutionContext.Implicits.global.execute(fut)

However this will not compile as execute only takes a Runnable. Is there a way I can execute this future immediately in a different thread?


Solution

  • Scala Futures created using Future.apply are eagerly executed.

    For non-eager execution, you will have to define your own class to indirectly create Future using Promise.

    import scala.concurrent.{Future, Promise}
    import scala.util.{Failure, Success, Try}
    
    class NonEagerFuture[A](computation: => A) extends Runnable {
      private val promise = Promise[A]
    
      override def run(): Unit = {
        Try(computation) match {
          case Success(result)    => promise.success(result)
          case Failure(exception) => promise.failure(exception)
        }
      }
    
      def isFutureCompleted(): Boolean =
        promise.isCompleted
    
      def getResult(): A =
        if (!promise.isCompleted) {
          throw new IllegalStateException("Future is not completed yet")
        } else {
          promise.future.value.get.get
        }
    
      def getScalaFuture(): Future[A] =
        promise.future
    
    }
    

    It can be used as following,

    println("creating the future")
    
    val nonEagerFuture = new NonEagerFuture({
      println("Doing some work")
      Thread.sleep(5000)
      println("Work done")
      println("returning 5 as result")
      5
    })
    
    println("running for the future")
    scala.concurrent.ExecutionContext.Implicits.global.execute(nonEagerFuture)
    
    // can also get the internal scala future and use it
    // this scala future will not have the eager problem 
    println("get the scala future") 
    val scalaFuture = nonEagerFuture.getScalaFuture()
    println("printing scalaFuture" + scalaFuture)
    
    // or block for completion using isFutureCompleted
    println("checking for the future")
    while (!nonEagerFuture.isFutureCompleted()) {
      Thread.sleep(1000)
      println("Checking completed: " + nonEagerFuture.isFutureCompleted())
    }
    
    println("getting result for the future")
    println("Result: " + nonEagerFuture.getResult())