0
votes

What is the pattern used in Scala to deal with the scenario:

You have a bunch of futures (they can be whatever, but for the sake of example...)

val aF = Future { true }
val bF = Future { Option(3) }
val cF = Future { myObject }

and you have some function that returns a future

def fooF: Future[SomeObject]

I want to do something like:

for {
    a <- aF
    b <- bF
    c <- cF
} yield {
    if (a) {
        // do stuff with b & c
        fooF
    } else {
        Future.successful(SomeObject)
    }
}

I want to return a value of Future[SomeObject], but I call fooF inside of the yield statement, I will get a Future[Future[SomeObject]]

2
Assign the "inner" Future in the for using <- before yield. Try to refactor with nested flatMap to understand. - cchantep
@cchantep won't that trigger the future before I use it though? I don't want the future to begin executing until I call it...which may be never - clo_jur
You should read more about for comprehension/flatMap/Future - cchantep
@clo_jur futures are triggered at evaluation anyway, so it will execute at this point, whether you put it in the for or in the yield. The only way to delay execution is to declare this whole Future[SomeObject] as lazy val or def, but it will also delay all the other futures in the computation (unless they were started beforehand). - Cyrille Corpet

2 Answers

2
votes

Here is another solution :

def doStuffWith(a: A, b: B, c: C): Future[SomeObject] = if (a) {
  // do stuff with b & c
  fooF
} else Future.successful(SomeObject)

for {
  a <- aF
  b <- bF
  c <- cF
  d <- doStuffWith(a, b, c)
} yield d

As discussed in @laughedelic answer, this is a subjective view, but I believe this way to be more readable, and maintainable (taking out the function always to unit test it, for instance).

-2
votes
  • In Scala 2.12 Future has a flatten method:

    Creates a new future with one level of nesting flattened, this method is equivalent to flatMap(identity).

    So you can write for { ... } yield { ... } flatten.

  • In Scala <2.12 you can achieve the same with flatMap(identity) (as mentioned above)


An alternative solution is to use flatMap instead of for-comprehension:

aF.flatMap { a =>
  if (a) {
    (bF zip cF).flatMap { (b, c) =>
      // do stuff with b & c
      fooF
    }
  } else 
    Future.successful(SomeObject)
  }
}