Question

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...

Was it helpful?

Solution 2

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.

OTHER TIPS

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
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top