Reputation: 345
I'm using the scala dispatch (0.11.0) library to send an HTTP GET request to a remote server. I want to wait for the response before executing the code which follows the request.
My request is of the form :
val req = :/("myurl.com") <:< myheaders OK as.Response(identity)
If I write :
val future = http(req)
future()
var res: String = null
future onComplete {
case Success(r) => res = r.getResponseBody
case _ => println("KO")
}
println(res)
I get null. This is also the case if I write :
val future = http(req)
var res: String = null
while (!future.isCompleted) {
Thread.sleep(1000)
}
future onComplete {
case Success(r) => res = r.getResponseBody
case _ => println("KO")
}
println(res)
But with the following code :
val future = http(req)
var res: String = null
future onComplete {
case Success(r) => res = r.getResponseBody
case _ => println("KO")
}
while (!future.isCompleted) {
Thread.sleep(1000)
}
println(res)
I get the expected response.
Does someone understand this ? It seems to me that calling Thread.sleep is not a good thing, could someone give me a hint on how I should handle this problem correctly ?
EDIT: @Randal Schulz thank you for your help, but as you posted in comments, I can't validate your answer.
As my problem was to wait (and do nothing else) until I get a valid response to an HTTP GET request, I think a satisfying way to do it is using Await.result. I removed the side-effect from my code. I used the option method to deal with Future failures (as I was interested only in successes), and I dealt with time out exception the classic way.
I think I could do it as wheaties mentionned, staying in Future, but I need more practice...
Upvotes: 1
Views: 1894
Reputation: 345
I finally managed to write what I wanted using futures :
def loop(): Future[String] = {
val future = http(req).option
future flatMap ((x: Option[Response]) => x match {
case Some(rep) => rep.getResponseBody
case None => loop()
}
}
Now I can use the result of this function without explicitely waiting for the response to come.
Upvotes: -1
Reputation: 35970
TL;DR
The best advice I can give you for working in an asynchronous work flow is that what goes into a Future
stays in a Future
.
Answer
The issue is that you have no idea when the Future
will complete so if you want to use an asynchronous process, you're going to have to write in an asynchronous manner. The code as you have written never stops or blocks on the Future
you create so the minute it creates the Future
and hand it off to another thread, the current thread is free to then evaluate the res
variable.
Hence, place most of what you're doing in a flow like follows:
myFuture map (func1) map (func2) map (func3) onComplete{
case Success(value) => println(value.getResponseBody)
case _ => println('KO')
}
Don't attempt to access something via side-effect like you are.
If you're really clever and you have several Future
you can compose them:
val f1 = myFuture map(func1)
val f2 = myOtherFuture map(func2) map (func3)
val f3 = for{
v1 <- f1
v2 <- f2
} yield functionTakingBoth(v1, v2)
f3 onComplete{
//and do stuff here
}
Upvotes: 3