Search code examples
scalaasynchronousconcurrencyblockingfuture

Is blocking within a future still blocking?


Blocking bad, async good, but is blocking within a future still blocking? This is something that I keep coming back to; consider following pseudo-code:

def queryName(id:Id):Future[String]
def queryEveryonesNames:Future[Seq[String]] = {
  val everyonesIds:Future[Seq[Id]] = getIds
  val everyonesNames:Future[Seq[Future[String]]] = {
    everyonesIds.map(seq.map(id=>queryName(id)))
  }
  // I'm trying to understand the impact of what I'll do below
  everyonesNames.map(seq=>seq.map(fut=>blocking(fut, 1 s)))
}
queryEveryonesNames

In the last line I turned Future[Seq[Future[String]]] (notice future within future) into Future[Seq[String]] by blocking on the inner future.

Blocking on a future within a future feels redundant, at least here, yet having a future within a future feels redundant as well.

Can you propose a smarter way of getting rid of the inner future?

Do you think blocking on a future inside a future is bad? If so why and under what circumstances?


Solution

  • Yes, future blocking is blocking, you should avoid that, as resources will be blocked to wait for a result, even if they are in another thread.
    If I understood correctly, your question is how to convert Future[Seq[Future[String]]] into Future[Seq[String]] in a non-blocking way.

    You can do that with for-comprehensions:

    val in =  Future[Seq[Future[String]]]
    val m = for( a <- in ) // a is Seq[Future[String]]
    yield ( Future.sequence(a)) // yields m = Future[Future[Seq[String]]]
    val result = for(a <- m; b <- a) yield (b) // yields Future[Seq[String]]
    

    EDIT: Or just:

    val result = in.flatMap(a => Future.sequence(a))