ps0604 ps0604 - 15 days ago 9
Scala Question

Running futures sequentially

The objective of the code below is to execute Future

f3
or
f4
depending on a condition. Note that the condition depends on the result of Future
f1
or
f2
, so it has to wait. This seems to work, however since
f1
and
f2
are futures this code shouldn't run sequentially. Is this code correct?

object TestFutures extends App {

val f1 = Future { 1 }
val f2 = Future { 2 }
val f3 = Future { 3 }
val f4 = Future { 4 }

val y = 1

for {
condition <- if (y>0) f1 else f2
_ <- if (condition==1) f3.map {a => println("333")} else f4.map {b => println("444")}
} yield ()


Thread.sleep(5000)
}

Answer

No it is not correct. When you create a Future like you do it, it starts the computations immediately. Before reaching for comprehension, all of your 4 futures are running already. You need to create them later, depending on the conditions.

val y = 1
for {
  condition <- if (y > 0) Future { 1 } else Future { 2 }
  _ <- if (condition == 1) 
    Future { 3 }.map(a => println("333"))
  else 
    Future { 4 }.map(b => println("444"))
} yield ()

It is probably good to extract creating each of those to a method, that you will just call, for sake of readability.

It should be obvious they start running when they are created because you can just say

Future(1).map(x => println(x))

and it works without any sort of triggering. Anyway try to run the following code

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def printWhenCompleted[A](f: Future[A]): Future[A] = f.map { x =>
    println(x)
    x
}

val f1 = printWhenCompleted(Future { 1 })
val f2 = printWhenCompleted(Future { 2 })
val f3 = printWhenCompleted(Future { 3 })

for {
  r3 <- f3
  r2 <- f2
  r1 <- f1
} yield r1 + r2 + r3

it should give you those numbers in random order, instead of sequential 3, 2, 1