Create Future without starting it
Asked Answered
S

8

8

This is a follow-up to my previous question

Suppose I want to create a future with my function but don't want to start it immediately (i.e. I do not want to call val f = Future { ... // my function}.

Now I see it can be done as follows:

val p = promise[Unit]
val f = p.future map { _ => // my function here }

Is it the only way to create a future with my function w/o executing it?

Smile answered 15/5, 2013 at 20:44 Comment(2)
Maybe you could say what it is you need to accomplish?Eyebolt
the fact that you want to delay the computation leads me to think you're relying on some external state change (side-effect). If this is the case, use actors...Gasoline
A
7

You can do something like this

val p = Promise[Unit]()
val f = p.future

//... some code run at a later time
p.success {
// your function
}

LATER EDIT:

I think the pattern you're looking for can be encapsulated like this:

class LatentComputation[T](f: => T) {
  private val p = Promise[T]()

  def trigger() { p.success(f) }

  def future: Future[T] = p.future
}

object LatentComputation {
  def apply[T](f: => T) = new LatentComputation(f)
}

You would use it like this:

val comp = LatentComputation {
// your code to be executed later
}

val f = comp.future

// somewhere else in the code
comp.trigger()
Ampliate answered 15/5, 2013 at 21:32 Comment(3)
I guess success may start my function immediately. What if I do want to start it later by calling success (or any other method) explicitly ?Smile
The idea is nice but you cannot chain the LatentComputation together as a monad for "for" expressions, it would be good if one computation finished it triggered the next one in a for expressionZamia
You would do this with the future from the LatentComputation. Just create an instance and pull out the future and use it in for comprehensions. Otherwise, just use a promise to accomplish thisAmpliate
W
3

You could always defer creation with a closure, you'll not get the future object right ahead, but you get a handle to call later.

type DeferredComputation[T,R] = T => Future[R]

def deferredCall[T,R](futureBody: T => R): DeferredComputation[T,R] =
  t => future {futureBody(t)}

def deferredResult[R](futureBody: => R): DeferredComputation[Unit,R] =
  _ => future {futureBody}
Wilmawilmar answered 16/5, 2013 at 6:52 Comment(0)
D
2

If you are getting too fancy with execution control, maybe you should be using actors instead?

Or, perhaps, you should be using a Promise instead of a Future: a Promise can be passed on to others, while you keep it to "fulfill" it at a later time.

Dragonet answered 15/5, 2013 at 22:21 Comment(1)
+1. People have commented that they're on the fence about futures vs actors for that reason. However, I find that when I'm getting too fancy, it's because I don't know what I'm doing. Sometimes I don't even know why I'm doing it. And yet I keep doing it.Eyebolt
E
2

It's also worth giving a plug to Promise.completeWith.

You already know how to use p.future onComplete mystuff.

You can trigger that from another future using p completeWith f.

Eyebolt answered 15/5, 2013 at 22:44 Comment(0)
I
2

You can also define a function that creates and returns the Future, and then call it:

val double = (value: Int) => {
  val f = Future { Thread.sleep(1000); value * 2 }
  f.onComplete(x => println(s"Future return: $x"))
  f
}

println("Before future.")
double(2)
println("After future is called, but as the future takes 1 sec to run, it will be printed before.")

I used this to executes futures in batches of n, something like:

// The functions that returns the future.
val double = (i: Int) => {
  val future = Future ({
    println(s"Start task $i")
    Thread.sleep(1000)
    i * 2
  })

  future.onComplete(_ => {
    println(s"Task $i ended")
  })

  future
}

val numbers = 1 to 20

numbers
  .map(i => (i, double))
  .grouped(5)
  .foreach(batch => {
    val result = Await.result( Future.sequence(batch.map{ case (i, callback) => callback(i) }), 5.minutes )
    println(result)
  })
Iaria answered 2/8, 2019 at 21:47 Comment(0)
G
1

Or just use regular methods that return futures, and fire them in series using something like a for comprehension (sequential call-site evaluation)

Gasoline answered 2/7, 2014 at 22:14 Comment(0)
V
1

This well known problem with standard libraries Future: they are designed in such a way that they are not referentially transparent, since they evaluate eagerly and memoize their result. In most use cases, this is totally fine and Scala developers rarely need to create non-evaluated future.

Take the following program:

val x = Future(...); f(x, x) 

is not the same program as

f(Future(...), Future(...))

because in the first case the future is evaluated once, in the second case it is evaluated twice.

The are libraries which provide the necessary abstractions to work with referentially transparent asynchronous tasks, whose evaluation is deferred and not memoized unless explicitly required by the developer.

  1. Scalaz Task
  2. Monix Task
  3. fs2

If you are looking to use Cats, Cats effects works nicely with both Monix and fs2.

Vito answered 26/2, 2018 at 11:9 Comment(0)
O
1

this is a bit of a hack, since it have nothing to do with how future works but just adding lazy would suffice: lazy val f = Future { ... // my function} but note that this is sort of a type change as well, because whenever you reference it you will need to declare the reference as lazy too or it will be executed.

Oder answered 2/8, 2018 at 7:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.