Starting playing with Scala futures, I get stuck with dependent futures.
Let's get a example. I search for places and get a Future[Seq[Place]]
. For each of theses places, I search for the closest subway stations (the service resurns a Future[List[Station]]
).
I would write this:
Place.get()
.map { places =>
places.map { place =>
Station.closestFrom(place).map { stations =>
SearchResult(place, stations)
}
}
}
That thing will make me get a Future[Seq[Future[SearchResult]]]
... which is... not what I would have expected.
What did I miss to get a Future[Seq[SearchResult]]
?
Thanks for all,
Alban
You are missing two Future
concepts in your solution: flatMap
and Future.sequence
To explain each:
flatMap
is like map
except instead of giving it a function from future.map(A => B)
you give it a function from future.flatMap(A => Future[B])
. This way you can chain Futures together.
Future.sequence
is a helper function that combines a list of futures to a future of a list: Seq[Future[A]] => Future[Seq[A]]
Using these two features of the Future
API we can change your answer to be:
Place.get().flatMap { places =>
Future.sequence(places.map { place =>
Station.closestFrom(place).map { stations =>
SearchResult(place, stations)
}
})
}
Working with futures is generaly easier using for-comprehension rather than directly map/flatMap. In your situation it should look like this:
for {places <- Place.get()
searchResults <- Future.traverse(places)(place => for (stations <- Station.closestFrom(place))
yield SearchResult(place,stations)
)
} yield searchResults
Future
being a monad, it offers you several ways to chain your operations.
f : A => B
to what's inside the box myfuture : Future[A]
, indeed map
is the way to get a Future[B]
. But in the present situation Station.closestFrom
a does not give you a List[Stattion]
but a Future[List[Station]]
. h : A => Future[B]
or chain several of them (here Places.get
and Station.closestFrom
), flatMap
is the way to go. Apply h
to a Future[A]
gives you a Future[B]
. h : A => Future[B]
to a collection like a places : Seq[A]
, you should use Future.traverse : Seq[A] => (A => Future[B]) => Future[Seq[B]]
. Furthermore, Scala's for-compresention is just syntactic sugar for flatMap/map so instead of writing complex code using those directly you can use a clean and clear for loop. The loop:
for { variable1 <- f1
variable2 <- f2
} yield expression
is equivalent to (without optimisations) :
f1.flatMap( variable1 => f2.map(variable2 => expression))
Don't hesitate to use for-comprehension, it really helps.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.