Reputation: 1941
I'm a beginner to Scala Futures
/Promises
.
I'm trying to do this using Futures
(using callbacks):
Here is the code:
// Final write container to hold the result.
val promise = Promise[List[GitData]]()
val repositoriesF = Future {
GitDriver.repositoriesOf(request.user)
}
val gitRepositories = Promise[List[GitRepository]]()
repositoriesF onSuccess {
case repositories => {
val contributors = Promise[List[Contributor]]()
val contributorsF = Future(repositories.map(GitDriver.contributors))
val readMe = Promise[List[String]]
val readMeF = Future(repositories.map(GitDriver.readme))
contributorsF.onSuccess {
case contrib => contributors.success(contrib.flatten)
}
readMeF.onSuccess {
case r => readMe.success(r)
}
val extractedContributors = contributors.future.value
val extractedReadme = readMe.future.value
println(extractedContributors.size)
println(extractedReadme.size)
}
}
Await.ready(repositoriesF, Duration.Inf)
The sizes of the contributors and readme are always zero.
My approach to this problem is, I can parallalize the extraction of contributors and README through Futures
as they don't depend upon each other. All they need is just a repository object.
What am I missing here? I'm sure that there are more elegant solutions using for comprehensions, maps, dsls etc. But just curious to get to the bottom of this one! Thanks.
Upvotes: 1
Views: 512
Reputation: 9820
Your future.value
and println
lines are executed before the Futures
(the explicit with Future(...)
and the Promise.future
set in onSuccess
) are completed.
The value of the Future
is still None
, compare with :
val p = Promise[Int]()
p.future.value
// Option[scala.util.Try[Int]] = None
p.future.value.size
// Int = 0
Since you want to continue with your solution, you could do something like (without the superfluous Promises
):
repositoriesF onSuccess {
case repositories =>
val contributorsF = Future(repositories.flatMap(GitDriver.contributors))
val readMeF = Future(repositories.map(GitDriver.readme))
contributorsF zip readMeF onSuccess {
case (contrib, readme) =>
println(contrib.size)
println(readme.size)
}
}
Upvotes: 0
Reputation: 5999
You are doing a few weird things. The main issue as per your problem is that you are printing the values inside this block:
repositoriesF onSuccess {
case repositories => {
...
So in a sense, you are only waiting for the FIRST async computation, and have only set callbacks for the other information you need (readme and contributors). Right after setting the callbacks, those are not completed and you have no results.
The second weird thing is that you are doing Await()
on the first future and not on the other computations. Most likely you want to wait on the entire information to be available.
The third weird thing you are doing is using too many promises. You use a Promise not when you want to get some result, but when you have to return a Future to someone else and need a way to complete that future later on, when you no longer have access to the future itself.
As for how to solve your problem, I would chain futures using for comprehensions:
val futureGitData = for {
repos <- Future { GitDriver.repositoriesOf(request.user) }
repo <- repos
readme <- Future { GitDriver.readme(repo) }
contributors <- Future { GitDriver.contributors(repo) }
} yield (repo, readme, contributors)
Note that this has much better parallelism, as I'm creating different futures for each repository. What you where doing, Future(repositories.map(GitDriver.readme))
would do the map synchronously and return a single future.
Note that I don't have Scala in this device, so that code might not be perfect, but should give you an idea.
Upvotes: 2
Reputation: 133
At the moment you print the size of the contributors and readme the future is probably not yet completed so the contributors.future.value
returns None.
You could try something like this:
val repositoriesF = Future {
GitDriver.repositoriesOf(request.user)
}
val contributorsAndReadmeFuture = for {
repositories <- repositoriesF
contributors <- Future(repositories.map(GitDriver.contributors))
readme <- Future(repositories.map(GitDriver.readme))
} yield (contributors, readme)
contributorsAndReadmeFuture onSuccess {
case (constributors, readme) =>
println(constributors.size)
println(readme.size)
}
Upvotes: 0