le-doude
le-doude

Reputation: 3367

Future[Future[T]] to Future[T] within another Future.map without using Await?

This is a theoretical question. I have a service I can call to do a job, but that service might not do all of it, and thus I will need to call a second one to finish it.

I was wondering if there is a way to do something similar to this without Await.result the result within the map function:

val myFirstFuture = asyncRequestA()

myFirstFuture.map(result => {
    result match {
       case isWhatIExpected => result
       case isNot => Await.result(asyncRequestB(), someDuration)
    }
})

I would like to "merge" the future given by asyncRequestB() into myFirstFuture without using the Await function to get the result.

Any ideas?

Upvotes: 3

Views: 1352

Answers (2)

Régis Jean-Gilles
Régis Jean-Gilles

Reputation: 32719

Just use flatMap instead of map:

myFirstFuture.flatMap{ result =>
  result match {
     case isWhatIExpected => Future.successful( result )
     case isNot => asyncRequestB()
  }
}

As a side note, you can even shorten it like this:

myFirstFuture.flatMap{
   case result: isWhatIExpected => Future.successful( result )
   case _ => asyncRequestB()
}

See also Nikita Volkov's answer for an example of using for comprehensions

Upvotes: 5

Nikita Volkov
Nikita Volkov

Reputation: 43309

Future is a monad. And the standard way of working with monads in Scala is using a "for comprehension":

for {
  firstResult <- firstFuture
  secondResult <- firstResult match {
    case isWhatIExpected => Future.successful( firstResult )
    case isNot => asyncRequestB()
  }
}
yield secondResult

In Scala "for comprehension" is a syntactic sugar for a series of flatMap, map and filter method applications and under the hood the compiler will expand this comprehension into the same thing as in Regis' answer.

Although in this case you might not exactly see the benefit from using a "for comprehension" syntax, when you drop in another Future and when things start getting trickier, it will come to shine. What "for comprehension" basically does is it flattens the otherwise many levels of nesting of those three method applications.

Secondly, there's also a way you could optimize Regis' solution syntactically, by utilizing "partial functions":

myFirstFuture.flatMap{ 
  case r if isWhatIExpected( r ) => Future.succesful( r )
  case r if isNot( r ) => asyncRequestB()
}

Upvotes: 6

Related Questions