The following seems to work and 'looks' functional. I have limited knowledge of how this will actually perform or act under the hood.
This could easily be slapped into a def where the hard coded 4 is passed a parameter.
I figured that I should return 5 elements even though only 4 were requested since evaluation of the middle future needs to happen either way. Dropping the superfluous elements should be simple.
val f = Iterable(Future(Seq(1,2,3)), Future(Seq(4,5)), Future(Seq(6,7,8)))
val output = f.foldLeft(Future(Seq.empty[Int])){(previous, next) =>
previous.flatMap{pSeq =>
if(pSeq.length >= 4) {
Future(pSeq)
} else {
next.map(nSeq => pSeq ++ nSeq)
}
}
}
println(Await.result(output, Duration.Inf)) //List(1,2,3,4,5)
The bit I don't like is wrapping the pSeq in a Future just to maintain consistent types.
EDIT: Just a response to viktor's answer (I can't comment since not high enough rep and it adds value to my answer slightly).
Even though Viktor's answer is easier to read it must wait for all Futures to complete even if they aren't required.
For example replace f in mine with the following:
val f = Iterable(Future(Seq(1,2,3)), Future(Seq(4,5)), Future(throw new Exception))
It will still work, Viktor's calls Future.sequence which turns a an Iterable[Future[]] int an Future[Iterable[]] and therefore all must be completed.
Iterable
ofFuture
s, they have already "started". – Ende NeuIterable
, and not aSeq
, will they really be started? I will initialize them "on demand", shouldn't this help? – roman-romanval f1: Future[_]
it means the future is already working. My 2 cents would be to have aSeq
of functions (depends on your logic and on what you're trying to do here) and then wrap them into a future while iterating the seq using something like the answer below. – Ende Neu