Asynchronous interfaces in Dispatch 0.7

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Asynchronous interfaces in Dispatch 0.7

n8han
Administrator
This post was updated on .
It's pretty easy to do anything concurrently in Scala as it is; you can use scala-lib's actors and futures or any of the other implementations cropping up. BUT some other HTTP interfaces have gotten a positive reception for built-in asynchronous interfaces so I decided to make a go at it.

It's a good thing I did because I learned something immediately: using separate HttpClient instances in separate threads is okay, but using a thread-safe connection manager is even better because it can reuse connections. You can do this with the existing Dispatch by using the singleton Http instance, but, there wasn't an easy way to use that with your own Http subclass.

An asynchronous interface needs to have a thread-safe connection manager (unless you want to create a new client object for every call). So, the way this is implemented is you get a "future" interface and thread-safe connection manager in one go, by mixing in a Threads trait.

val h = new Http with Threads
val f = h.future(:/("example.com") as_str)

That gives you back a future, which is assigned a structural type:

  type Future[T] = Function0[T] {
    def isSet: Boolean
  }

It is not actually, currently, implemented with the scala.actors future, but with the java.util.concurrent. If you try to nest futures based on actors, like

h.future(request >- { str => h.future(request2 <<? Map("query" -> str) as_str) })

it will fail, and, that's something you might want to do.

What happens if there is an exception in the future??? That is interesting. A wrapping ExecutionException will be thrown if you do the blocking apply() to get the value. But sometimes you aren't retrieving the value or you want to take action immediately when there's an exception. So there's a second interface for that:

h on_error {
    case e => println("alert!")
} future (:/("example.com") as_str)

The method on_error takes a PartialFunction[Throwable, Unit], and it causes the request execution to take place in a try-catch block. When an exception is thrown that the partial function is defined for, it is called.

It ends up looking just like a regular exception handling block in Scala except for one big difference: the exception is still always re-thrown after, by Threads. Why? Because request execution must return a correctly typed value or throw an exception. The partial function can't be typed to the return value of the Request because, as you can see, it's not part of Request but rather Http with Threads. The Request class is an abstraction for expected behavior and Dispatch throws StatusCode exceptions outside its scope. This is all very complicated and not necessary to understand at all, but it's the reason that on_error operates as an observer. If you are planning to access the future's value from the main thread, you probably don't need on_error since you'll have to deal with exceptions when trying to get the value anyway.

This is published as Dispatch 0.7.0-beta1 and it lives on a 0.7 branch of the repository for the moment. Any ideas for improvement (or problems foreseen) before the interface is finalized?

Nathan