bsky
bsky

Reputation: 20242

For comprehension - execute futures in order

If I have the following for comprehension, futures will be executed in order: f1, f2, f3:

   val f = for {
      r1 <- f1
      r2 <- f2(r1)
      r3 <- f3(r2)
    } yield r3

For this one however, all the futures are started at the same time:

  val f = for {
      r1 <- f1
      r2 <- f2
      r3 <- f3
    } yield ...

How can I enforce the order?(I want this order of execution f1, f2, f3)

Upvotes: 1

Views: 1954

Answers (3)

Dima
Dima

Reputation: 40508

It does matter what f1, f2, f3 are: a future will start executing a soon as it is created. In your first case, f2(r1) must be a function returning a future, so the future begins executing when the function is called, which happens when r1 becomes available.

If the second case is the same (f2 is a function), then behavior will be the same as the first case, your futures will be executed sequentially, one after the other.

But if you create the futures outside the for, and just assign them to variables f1, f2, f3, then by the time you get inside the comprehension, they are already running.

Upvotes: 3

mgosk
mgosk

Reputation: 1876

Executing futures in for comprehension is default behavior. It is good when few tasks are processed parrallel without any blocking.

But if you want to preserve procecessing order you have to ways:

  • Send result of first task to second like in your example
  • use andThen operator

    val allposts = mutable.Set[String]()    
    Future {
      session.getRecentPosts
    } andThen {
      posts => allposts ++= posts
    } andThen {
      posts =>
      clearAll()
      for (post <- allposts) render(post)
    }
    

Upvotes: 2

wheaties
wheaties

Reputation: 35990

Future are eager constructs, that is, once created you can not dictate when they get processed. If the Future already exists when you attempt to use it in a for-comprehension, you've already lost the ability to sequence it's execution order.

If you want to enforce ordering on a method that accepts Future arguments then you'll need to wrap the evaluation in a thunk:

def foo(ft: => Future[Thing], f2: => Future[Thing]): Future[Other] = for{
  r1 <- ft
  r2 <- f2
} yield something(r1, r2)

If, on the other hand, you want to define the Future within a method body, then instead of val use a def

def foo() ={
  def f1 = Future{ code here... }
  def f2 = Future{ code here... }
  for{
    r1 <- f1
    r2 <- f2
  } yield something(r1, r2)

Upvotes: 2

Related Questions