1

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

Urist McDev
  • 498
  • 3
  • 14
ygu
  • 345
  • 1
  • 16
  • 1
    Use `Await.ready` (returns the `Future` itself) or `Await.result` (returns the value computed by the `Future`) and be prepared for the possible exception due to timeout or failure of the `Future` itself. – Randall Schulz Jan 30 '14 at 15:05
  • Well thank you for pointing these out, because I didn't realize that Future was not a dispatch concept. Nevertheless, using Await.result, I get the same result than before : / – ygu Jan 30 '14 at 15:34
  • You showed three attempts (only one of which yielded "the expected response"). Which of these are you referring to when you say you get "the same result [as] before?" – Randall Schulz Jan 30 '14 at 15:44
  • Sorry, I was talking about the wrong result of course, either I would have told you. – ygu Jan 30 '14 at 15:46
  • 1
    Well, one thing I should have said at the beginning is that you most certainly should not set a variable defined at a scope outside the Future from within it! Most likely the problem you have now is processor cache incoherence. Instead you should have the Future yield the result as its value, not via that kind of side-effect. – Randall Schulz Jan 30 '14 at 15:59

2 Answers2

3

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
}
wheaties
  • 35,646
  • 15
  • 94
  • 131
  • Completely avoiding blocking is often not feasible depending on the other libraries you're working with (conventional, not nio-based I/O, DNS, DBMS interactions, etc.). I've never found it adequate to simply say "don't do that" when it comes to blocking operations or waiting on `Future` completion. – Randall Schulz Jan 30 '14 at 15:26
  • Ok, I was wrong... If I understand well, all my program should be in Future, as it all depends on the first instruction which is an asynchronous one (a HTTP GET request), right ? – ygu Jan 30 '14 at 15:41
  • 1
    @RandallSchulz generally, I write the code in a non-async manner and just wrap it in `Future` so that the system is not tied to a `Future` from top to bottom. Moreover, for performance, I split CPU intensive and I/O intensive things out into different thread pools if possible. That is, essentially, the beauty of `Future`. You can wrap anything in it and it never needs to know. – wheaties Jan 30 '14 at 16:25
-1

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.

ygu
  • 345
  • 1
  • 16
  • Your code is nothing else than wasting CPU time... If you really want to be synchronous, you should use e.g. `Await.result`. – Karel Horak Apr 27 '15 at 10:28